java mock 数据库_spring-mock + dbutil 用来测试数据库操作

本文介绍了一种使用Spring-Mock和DbUnit进行Java DAO层数据库操作测试的方法。首先通过DbUnit创建初始测试数据,然后利用Spring-Mock确保测试后的数据库状态回滚,保证原子性。接着,通过JUnit进行测试,最后DbUnit清理测试数据。测试步骤包括:设置初始数据、保存、更新、删除和查询用户,以及断言数据库状态。测试过程中,DbUnit从XML文件加载和比较数据集,而Spring的事务管理则保证了数据的回滚。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大概流程:1.用dbunit创建初始的测试数据。2.用spring-mock 维护测试过程中的数据会滚,这样可以保证测试后数据库保持原状态。3.用junit架构测试。4.用dbunit销毁初始测试数据。

Java代码 icon_copy.gificon_star.pngpackagecom.test.dbunit.dao;

importjavax.sql.DataSource;

importorg.dbunit.Assertion;

importorg.dbunit.database.DatabaseConnection;

importorg.dbunit.database.IDatabaseConnection;

importorg.dbunit.database.QueryDataSet;

importorg.dbunit.dataset.IDataSet;

importorg.dbunit.dataset.xml.FlatXmlDataSet;

importorg.dbunit.operation.DatabaseOperation;

importorg.junit.Assert;

importorg.junit.Before;

importorg.junit.Test;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.core.io.ClassPathResource;

importorg.springframework.jdbc.datasource.DataSourceUtils;

importorg.springframework.test.context.ContextConfiguration;

importorg.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;

importorg.springframework.test.context.transaction.TransactionConfiguration;

importcom.test.dbunit.entity.User;

@ContextConfiguration(locations = {"classpath:testApplicationContext.xml"})

@TransactionConfiguration(defaultRollback =true)

publicclassUserDaoTestextendsAbstractTransactionalJUnit4SpringContextTests {

@Autowired

privateUserDao userDao;

@Autowired

privateDataSource dataSource;

privateIDatabaseConnection conn;

@Before

publicvoidinitDbunit()throwsException {

conn =newDatabaseConnection(DataSourceUtils.getConnection(dataSource));

}

@Test

publicvoidsaveUser()throwsException {

User user =newUser();

user.setNick("user001");

user.setPassword("password001");

userDao.save(user);

QueryDataSet actual =newQueryDataSet(conn);

actual.addTable("user",

"select * from user where user.nick = 'user001'");

IDataSet expected =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001.xml").getFile());

Assertion.assertEquals(expected, actual);

}

@Test

publicvoidupdateUser()throwsException {

IDataSet origen =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001.xml").getFile());

DatabaseOperation.INSERT.execute(conn, origen);

User user =newUser();

user.setNick("user001");

user.setPassword("password002");

userDao.update(user);

QueryDataSet actual =newQueryDataSet(conn);

actual.addTable("user",

"select * from user where user.nick = 'user001'");

IDataSet expected =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001_updated.xml").getFile());

Assertion.assertEquals(expected, actual);

}

@Test

publicvoidremoveUser()throwsException {

IDataSet origen =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001.xml").getFile());

DatabaseOperation.INSERT.execute(conn, origen);

userDao.remove("user001");

QueryDataSet actual =newQueryDataSet(conn);

actual.addTable("user","select * from user where nick = 'user001'");

Assert.assertEquals(0, actual.getTable("user").getRowCount());

}

@Test

publicvoidfindUser()throwsException {

IDataSet data =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001.xml").getFile());

DatabaseOperation.INSERT.execute(conn, data);

User user = userDao.getUserByNick("user001");

Assert.assertEquals("password001", user.getPassword());

}

}

对Dao进行单元测试,一般有两种思路。一是Mock,对使用的底层API进行Mock,比如Hibernate和JDBC接口,判断接口有没有正确调用,另一种是实际访问数据库,判断数据库有没有正确读写。更多的情况下,我更倾向于后者,因为在使用ORM工具或者jdbcTemplate的情况下,dao一般只有简单的几行代码,没有复杂的逻辑,Mock测试一般没有什么意义,我们更关心的是,Hibernate mapping是否正确,ibatis sql是否正确等,所以实际读写数据库才能真正判断一个dao是否正确,这也是我们关心的测试内容。

好的单元测试应该是原子性的,独立的,不应依赖其他测试和上下文,但是要测试数据读写是否正确,就必须涉及初始数据的加载,数据修改的还原等操作。对于初始数据的加载,手动输入很麻烦,一个解决方案就是使用Dbunit,从Xml文件甚至Excel中加载初始数据到数据库,是数据库的值达到一个已知状态。同时还可以使用Dbunit,对数据库的结果状态进行判断,保证和期望的一致。数据修改的还原,可以依赖Spring TransactionalTests,在测试完成后回滚数据库。

Dbunit还可以对数据的现有数据进行备份,还原,清空现有数据,一个好的测试实践是每一个开发人员一个测试数据库,进而对数据库的数据状态有更好的控制,但现实可能会是共享同一个测试库,所以这种情况下,测试的编写必须多做一些考虑。

待测试的类:

Java代码 icon_copy.gificon_star.pngpackagecom.test.dbunit.dao.impl;

importjava.sql.ResultSet;

importjava.sql.SQLException;

importorg.springframework.jdbc.core.RowMapper;

importcom.test.dbunit.dao.UserDao;

importcom.test.dbunit.entity.User;

publicclassDefaultUserDaoextendsBaseDaoimplementsUserDao {

privatestaticString QUERY_BY_NICK ="select * from user where user.nick = ?";

privatestaticString REMOVE_USER ="delete from user where user.nick = ?";

privatestaticString INSERT_USER ="insert into user(nick,password) values(?, ?)";

privatestaticString UPDATE_USER ="update user set user.password = ? where user.nick = ?";

@Override

publicUser getUserByNick(String nick) {

return(User) getJdbcTemplate().queryForObject(QUERY_BY_NICK,newObject[]{nick},newRowMapper(){

@Override

publicObject mapRow(ResultSet rs,intindex)throwsSQLException {

User user =newUser();

user.setNick(rs.getString("nick"));

user.setPassword(rs.getString("password"));

returnuser;

}

});

}

@Override

publicvoidremove(String nick) {

getJdbcTemplate().update(REMOVE_USER,newObject[]{nick});

}

@Override

publicvoidsave(User user) {

getJdbcTemplate().update(INSERT_USER,newObject[]{user.getNick(), user.getPassword()});

}

@Override

publicvoidupdate(User user) {

getJdbcTemplate().update(UPDATE_USER,newObject[]{user.getPassword(), user.getNick()});

}

}

单元测试:

需要注意的地方就是,DataSourceUtils.getConnection(datasource), 通过这种方式获得数据库连接初始化Dbunit,能够保证Dbunit使用的数据连接和当前事务的数据库连接相同,保证能够在参与到事务中。Spring的TransactionManager会在开始事务时把当前连接保存到ThreadLocal中,DataSourceUtils.getConnection方法,首先从ThreadLocal中获取连接。

user001.xml

Xml代码 icon_copy.gificon_star.png<?xmlversion ="1.0"encoding="UTF-8"?>

使用dbunit,可以通过xml文件定义数据集,也可以使用其他方式定义,比如Excel,编程方式。

Dbunit的主要构件

IDatabaseConnection

IDataSet

数据集,数据集可以从Xml文件Excel等外部文件获取,也可以从数据库查询,或者编程方式构件,数据集可以作为初始数据插入到数据库,也可以作为断言的依据。另外还有IDatatable等辅助类。

比如在updateUser测试中,使用了QueryDataSet,从数据库中构建一个Dataset,再通过FlatXmlDataSet从Xml文件中构建一个Dataset,断言这两个Dataset相同。

Java代码 icon_copy.gificon_star.pngQueryDataSet actual =newQueryDataSet(conn);

actual.addTable("user","select * from user where user.nick = 'user001'");

IDataSet expected =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001_updated.xml").getFile());

Assertion.assertEquals(expected, actual);

DatabaseOperation

通过定义的静态字段可以获取一组代表一个数据操作的子类对象,比如DatabaseOperation .INSERT,返回 InsertOperation,通过执行execute方法把数据集插入到数据库。例如:

Java代码 icon_copy.gificon_star.pngIDataSet origen =newFlatXmlDataSet(newClassPathResource(

"com/taobao/dbunit/dao/user001.xml").getFile());

DatabaseOperation.INSERT.execute(conn, origen);

从Xml文件中构建DataSet,使用Insert插入到数据库,初始化测试数据。

Assertion

唯一的方法,assertEqual,断言两个数据集或数据表相同。

PS:使用Oracle的时候,初始化DatabaseConnection需要传入scheme。new DatabaseConnection(conn,SCHEMA_NAME ) ,SCHMEA_NAME需要大写。

附件提供所有代码下载

一个DAO测试基类

Java代码 icon_copy.gificon_star.pngpackagecom.taobao.dbunit.dao;

importjava.sql.SQLException;

importjavax.sql.DataSource;

importorg.dbunit.Assertion;

importorg.dbunit.database.DatabaseConnection;

importorg.dbunit.database.IDatabaseConnection;

importorg.dbunit.dataset.DataSetException;

importorg.dbunit.dataset.DefaultDataSet;

importorg.dbunit.dataset.DefaultTable;

importorg.dbunit.dataset.IDataSet;

importorg.dbunit.dataset.xml.FlatXmlDataSet;

importorg.dbunit.operation.DatabaseOperation;

importorg.junit.Assert;

importorg.junit.Before;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.core.io.ClassPathResource;

importorg.springframework.jdbc.datasource.DataSourceUtils;

importorg.springframework.test.context.ContextConfiguration;

importorg.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;

importorg.springframework.test.context.transaction.TransactionConfiguration;

@ContextConfiguration(locations = {"classpath:testApplicationContext.xml"})

@TransactionConfiguration(defaultRollback =true)

publicclassBaseDaoTestextendsAbstractTransactionalJUnit4SpringContextTests {

@Autowired

privateDataSource dataSource;

privateIDatabaseConnection conn;

@Before

publicvoidinitDbunit()throwsException {

conn =newDatabaseConnection(DataSourceUtils.getConnection(dataSource));

}

/**

* 清空file中包含的表中的数据,并插入file中指定的数据

*

* @param file

* @throws Exception

*/

protectedvoidsetUpDataSet(String file)throwsException {

IDataSet dataset =newFlatXmlDataSet(newClassPathResource(file)

.getFile());

DatabaseOperation.CLEAN_INSERT.execute(conn, dataset);

}

/**

* 验证file中包含的表中的数据和数据库中的相应表的数据是否一致

*

* @param file

* @throws Exception

*/

protectedvoidverifyDataSet(String file)throwsException {

IDataSet expected =newFlatXmlDataSet(newClassPathResource(file)

.getFile());

IDataSet dataset = conn.createDataSet();

for(String tableName : expected.getTableNames()) {

Assertion.assertEquals(expected.getTable(tableName), dataset

.getTable(tableName));

}

}

/**

* 清空指定的表中的数据

*

* @param tableName

* @throws Exception

*/

protectedvoidclearTable(String tableName)throwsException {

DefaultDataSet dataset =newDefaultDataSet();

dataset.addTable(newDefaultTable(tableName));

DatabaseOperation.DELETE_ALL.execute(conn, dataset);

}

/**

* 验证指定的表为空

*

* @param tableName

* @throws DataSetException

* @throws SQLException

*/

protectedvoidverifyEmpty(String tableName)throwsDataSetException,

SQLException {

Assert.assertEquals(0, conn.createDataSet().getTable(tableName)

.getRowCount());

}

}

使用:

Java代码 icon_copy.gificon_star.png@Test

publicvoidupdateUser()throwsException {

setUpDataSet("com/taobao/dbunit/dao/user001.xml");

User user =newUser();

user.setNick("user001");

user.setPassword("password002");

userDao.update(user);

verifyDataSet("com/taobao/dbunit/dao/user001_updated.xml");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值