前面介绍了不少写单元测试的内容,比方说Mockito和PowerMockito, JUnit 5,经常写单元测试的想必对这些框架都比较熟悉。
这篇博客主要介绍下数据库驱动测试框架–DbUnit(http://dbunit.sourceforge.net/), 主要从DbUnit的设计原理和实际使用来展开,这里的使用我又分为三个部分:
- 基于spring-test-dbunit的使用
- 基于dbunit本身api的使用
- 在dbunit的基础上整合了公司自己的jdbc框架完成的工具类
DBUnit 设计原理
看过我之前关于单元测试的博客和熟悉单元测试的开发人员都知道,在写单元测试时最重要的一点就是单元测试是要求可以反复执行验证的。
那么在我们对数据库进行单元测试的时候,为了保证每次数据库的单元测试都可以得到一个相同的结果,我们就不能直接使用数据库里的数据来进行测试验证,说不定什么时候数据就被别人修改了,而且我们的单测执行最好也不要对数据库的数据有什么修改 — 很容易就想到的数据库的事务特性。
但是考虑到有的数据库本身并不支持事务,比如MyISAM引擎,而由dbunit本身实现事务是比较复杂的,所以dbunit框架本身是没有实现事务的
dbunit的设计原理就是在执行测试用例之前,先备份数据库,然后向数据库中插入我们需要的初始化数据(准备数据),然后,在测试完毕后,清空表数据再将之前的备份的数据还原到数据库,从而回溯到测试前的状态。
乍一看是不是也像是实现了一个"事务" ?但还是有两个问题:
- 如果在单测执行过程中遇到问题, 导致执行中断,那么最后可能没有正常还原数据,这样的话就可能导致数据库的数据丢失(所以无论单测执行成功还是失败都记得一定要执行还原数据的代码)
- 单测执行过程中修改的数据在还原数据库的时候是会有丢失的,不过因为是测试环境的数据,影响也不是很大
DBUnit 基本概念和流程
基于DBUnit 单元测试的主要接口是IDataSet。IDataSet 数据集代表一个或多个表的数据。
dbunit可以将数据库的全部内容表示为IDataSet 实例。数据库表可以用ITable 实例来表示。
public interface IDataSet
{
/**
* 从IDataSet获取表名集合
*/
public String[] getTableNames() throws DataSetException;
/**
* 获取数据库指定表的元数据
*/
public ITableMetaData getTableMetaData(String tableName)
throws DataSetException;
/**
* 获取指定表
*/
public ITable getTable(String tableName) throws DataSetException;
/**
* 获取所有的表集合
*/
public ITable[] getTables() throws DataSetException;
}
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet实现为:
FlatXmlDataSet:数据的简单平面文件 XML 表示
QueryDataSet:用 SQL 查询获得的数据
DatabaseDataSet:数据库表本身内容的一种表示
XlsDataSet :数据的excel表示
我们使用DbUnit进行数据库单元测试的流程如下:
- 备份数据库中的表数据
- 准备好测试使用的初始化数据和预期的结果数据,一般用xml文件表示
- 清空数据表并导入初始化数据。
- 执行对应的测试方法,比较实际执行的返回结果与预期结果是否匹配
- 使用备份文件还原表数据
DBUnit 使用
spring 结合dbunit完成db测试
dbunit本身并没有提供事务支持的功能,但是spring是可以提供事务支持的,包括声明式事务和程序控制事务。所以dbunit结合spring可以将上述单元测试的执行全都放在一个事务里,这样就可以解决我上面提到的两个问题
如果结合spring使用dbunit进行单元测试,就需要引入dbunit和spring-test-dbunit两个jar包
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.0</version>
<type>jar</type>
<scope>test</scope>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ServiceInitializer.class)
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
@DbUnitConfiguration(databaseConnection={
"dataSource"})
@Transactional
public class BaseTest {
、
}
因为我们使用@DbUnitConfiguration注解传入了dataSource, 这样在dbunit里获取连接的时候得到就是从spring管理的数据源获取的connection,这样事务管理也可以由spring的声明式事务托管。
public class UserMapperDBUnitTest extends BaseTest {
@Autowired
private UserMapper userMapper;
@Test
@DatabaseSetup("/dbunit/sampleData_initdata.xml",type = DatabaseOperation.CLEAN_INSERT)
@ExpectedDatabase(value = "/dbunit/sampleData_result_insert.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)
public void testInsertSelective(){
User user = new User();
user.setId("2");
user.setUserName("Tom");
user.setAge(28);
user.setBirthday("1993-03-21"

2546

被折叠的 条评论
为什么被折叠?



