DbUnit是测试数据库的利器,不过要想弄明白还是需要一番研究。好在它的源代码不多,文档也还算全。我就在此做一个总结吧。
DbUnit.NET是DbUnit的.NET版,不过只推出了alpha版,而且自从06年以后就不再更新了。Stack Overflow上有一个帖子,提出了一些替代方案。
现在的DbUnit要求在测试时继承DBTestCase,而不是之前的DatabaseTestCase(前者继承自后者,而后者继承了junit的TestCase)。DatabaseTestCase包含两个抽象方法,getConnection()和getDataSet(),前者用来获取数据库连接,后者获取要测试的数据集。
数据集
DbUnit可以把所有表的记录存在一个数据集中:既可以是数据库中的表,也可以是文件中的数据。我们在此用FlatXmlDataSet来演示。
顺便提一句,DbUnit中还存在另一种格式的数据集XmlDataSet,它们的区别如下:
在FaltXmlDataSet对应的XML文件里,元素名称对应数据库表名,元素的属性(attribute)对应表的列。如:
<dataset>
<Person Name="Kirin" Age="31" Location="Beijing"/>
<Person Name="Jade" Age="30"/>
</dataset>要注意,如果数据库中某一条字段为null,在flat XML中将不会显示该attribute。另外,FlatXmlDataSet用XML文件中该表的第一行数据来制定表的结构。因此,如果数据库中某个字段所有记录都为null,或者恰巧第一条记录为null,那么得到的表结构与原数据库的表结构就不一致了,测试就会失败。FlatXmlDataSet中存在一个column sensing的概念,在从文件加载数据时,将该属性设置为true,就会根据第一行展现出来的表结构,自动将别的行的列补齐。
在XmlDataSet对应的XML文件里,用元素的子元素对应表的列。如:
<dataset>
<Person>
<Name>Kirin</Name>
<Age>31</Age>
<Location>Beijing</Location>
</Person>
<Person>
<Name>Jade</Name>
<Age>30</Age>
<Location/>
</Person>
</dataset>null用空元素来表示。
将数据库导出到XML文件
我们可以手写XML来准备数据,也可以从数据库中导出现有的数据,用FlatXmlDataSet.write()静态方法即可,例如:
QueryDataSet dataSet = new QueryDataSet(connection);
dataSet.addTable(TABLE_NAME, "select * from " + TABLE_NAME);
dataSet.addTable(...);
FlatXmlDataSet.write(dataSet, new FileOutputStream("data.xml"));重写getDataSet
有了文件数据,我们就需要重写getDataSet(),让它加载文件中的数据并返回。
@Override
protected IDataSet getDataSet() throws Exception {
// set column sensing as true, so it can dynamically and columns with null value.
return new FlatXmlDataSetBuilder()
.setColumnSensing(true)
.build(new FileInputStream("data.xml"));
}IDatabaseTester
DBTestCase重写了getConnection(),并把它设置为final,将获取connection的操作委托给IDatabaseTester,我们可以通过重写getDatabaseTester()方法来设置具体的IDatabaseTester。Dbunit中,IDatabaseTester的实现类一共有四个:
•DefaultDatabaseTester
•JdbcDatabaseTester
•DataSourceDatabaseTester
•JndiDatabaseTester
它们的用途不言自明。
DatabaseTestCase重写了TestCase里的setUp()和tearDown()方法。
protected void setUp() throws Exception
{
super.setUp();
final IDatabaseTester databaseTester = getDatabaseTester();
assertNotNull( "DatabaseTester is not set", databaseTester );
databaseTester.setSetUpOperation( getSetUpOperation() );
databaseTester.setDataSet( getDataSet() );
databaseTester.setOperationListener(getOperationListener());
databaseTester.onSetup();
}
protected void tearDown() throws Exception
{
try {
final IDatabaseTester databaseTester = getDatabaseTester();
assertNotNull( "DatabaseTester is not set", databaseTester );
databaseTester.setTearDownOperation( getTearDownOperation() );
databaseTester.setDataSet( getDataSet() );
databaseTester.setOperationListener(getOperationListener());
databaseTester.onTearDown();
} finally {
tester = null;
super.tearDown();
}
}可以看出它们的大体意图:为tester设置操作、数据集和监听器,然后执行相应的操作。获取数据集的是抽象方法,需要我们来实现。监听器主要负责在得到数据连接或setUp、tearDown结束后执行的操作,使用默认实现即可。我们主要来说说getSetUpOperation和getTearDownOperation返回的DatabaseOperation。
DatabaseOperation
DatabaseOperation定义了对数据库进行的操作,它是一个抽象类,通过静态字段提供了几种内置的实现:
•NONE:不执行任何操作,是getTearDownOperation的默认返回值。
•UPDATE:将数据集中的内容更新到数据库中。它假设数据库中已经有对应的记录,否则将失败。
•INSERT:将数据集中的内容插入到数据库中。它假设数据库中没有对应的记录,否则将失败。
•REFRESH:将数据集中的内容刷新到数据库中。如果数据库有对应的记录,则更新,没有则插入。
•DELETE:删除数据库中与数据集对应的记录。
•DELETE_ALL:删除表中所有的记录,如果没有对应的表,则不受影响。
•TRUNCATE_TABLE:与DELETE_ALL类似,更轻量级,不能rollback。
•CLEAN_INSERT:是一个组合操作,是DELETE_ALL和INSERT的组合。是getSetUpOeration的默认返回值。
由此我们可以总结出,在一个测试执行前后,DbUnit会为我们做哪些工作:
1.移除数据库中的所有记录(CLEAN_INSERT中的DELETE_ALL)。
2.将数据集中的数据加载到数据库中(CLEAN_INSERT中的INSERT)。
3.运行测试。
4.测试运行完毕后,不执行任何操作。
我们可以根据需要,在测试类中重写setUp和tearDown,以实现定制的需求。比如,数据库中已经有一些数据,我们不希望数据集中的数据对它们产生任何影响,这时可以先将数据库中的数据备份到内存中,等测试完成后再恢复到数据库中,代码如下:
private IDataSet dataSetBackup;
private static final String[] TABLE_NAMES = new String[] { "..." };
@Override
protected void setUp() throws Exception {
dataSetBackup = new CachedDataSet(getConnection().createDataSet(TABLE_NAMES));
super.setUp();
}
@Override
protected void tearDown() throws Exception {
try {
final IDatabaseTester databaseTester = getDatabaseTester();
assertNotNull( "DatabaseTester is not set", databaseTester );
databaseTester.setTearDownOperation( getTearDownOperation() );
databaseTester.setDataSet( dataSetBackup ); // 这里不使用getDataSet(),而是使用备份的数据库中数据
databaseTester.setOperationListener(getOperationListener());
databaseTester.onTearDown();
} finally {
tester = null;
dataSetBackup = null;
//super.tearDown(); // 这里不再调用基类的tearDown
}
}
@Override
protected DatabaseOperation getTearDownOperation() {
return DatabaseOperation.CLEAN_INSERT;
}测试前用CLEAN_INSERT,是用数据集覆盖数据库,测试后用CLEAN_INSERT,使用备份的数据库覆盖之前插入到数据库中的数据集。
完整的基类代码在这里。
好了,现在可以开始测试了。
分享到:
相关推荐
在数据库驱动的应用程序中,Dao层通常负责与数据库的交互,因此对Dao进行集成测试是非常必要的。 1. **配置Spring测试环境** 在Spring项目中,我们需要创建一个测试配置类,该类通常继承自`...
2. 数据库操作:它提供了一系列API来填充数据库(通常称为“setup”)、清理数据库(“teardown”)以及在测试之间执行数据比较(“assertion”)。这些操作确保了测试环境的一致性。 3. 数据对比:DbUnit 可以对...
DbUnit 入门实战 DbUnit 是一个专门针对数据库... DbUnit 的设计理念和使用方法可以帮助开发人员更好地进行数据库测试,提高测试的效率和质量。同时,DbUnit 也可以与其他测试框架集成,提供更加全面的测试解决方案。
在软件开发中,确保数据库状态的一致性和准确性是至关重要的,尤其是在进行集成测试时。DBUnit 提供了一种简单且结构化的机制来导入和导出数据库数据,帮助开发者有效地管理和维护测试数据。 ### DBUnit 的基本概念...
2. **操作数据库**:DBUnit通过`IDatabaseConnection`接口与数据库进行交互。它提供了`setup`、`teardown`、`assertTableEquals`等方法,分别用于在测试前清空表、测试后恢复原状,以及比较数据库表的实际状态与期望...
它作为 JUnit 的扩展,提供了强大的数据导入和导出功能,使得开发者可以在测试前后对数据库进行清空、填充和验证操作,从而确保测试的一致性和隔离性。 DbUnit 支持多种数据格式,如 XML、CSV 和 Excel,可以方便地...
然后在测试的setUp阶段,将数据导入到数据库,而在tearDown阶段,清除这些数据。这样,即使测试用例之间有数据依赖,也能保证每个测试用例独立且可重复执行。 总之,为了提升代码质量,开发者应重视可重复的系统...
4. **数据比较**:测试结束后,DbUnit 使用预定义的预期数据集与数据库的实际状态进行比较,如果两者一致,测试通过;否则,测试失败。 5. **清理**:最后,DbUnit 可以选择性地清除所有在测试期间修改的数据,以...
总的来说,通过集成 DBUnit,Spring Boot 应用可以实现高效的数据库测试,确保在开发过程中数据操作的正确性。这有助于提高代码质量,减少因数据库问题导致的生产故障。在实际项目中,应根据项目需求和团队规范调整...
通过 "tutorial-dbunit-example" 项目,我们可以学习到如何在 Maven 项目中集成和使用 dbUnit,实现数据库的初始化、数据加载以及状态验证。这有助于提高测试的覆盖率和质量,确保代码在不同环境下的行为一致性。在...
5. **执行目标**:在Maven命令行中运行相应的生命周期阶段,如`mvn dbunit:setup`来准备测试数据,`mvn dbunit:teardown`来清理数据。 **注意事项** 1. **事务管理**:为了确保数据一致性,通常在操作数据库时会...