51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1940|回复: 0
打印 上一主题 下一主题

用DbUnit进行数据库集成测试

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-5-21 16:36:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
DbUnit是测试数据库的利器,不过要想弄明白还是需要一番研究。好在它的源代码不多,文档也还算
全。我就在此做一个总结吧。

DbUnit.NET是DbUnit的.NET版,不过只推出了alpha版,而且自从06年以后就不再更新了。Stack Ove
rflow上有一个帖子,提出了一些替代方案。

现在的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,那么得到的表结构与原数据库的表结构就不一致了,测试就会失败。FlatXm
lDataSet中存在一个column sensing的概念,在从文件加载数据时,将该属性设置为true,就会根据第
一行展现出来的表结构,自动将别的行的列补齐。

在XmlDataSet对应的XML文件里,用元素的子元素对应表的列。如:

  1. <dataset>
  2.     <Person>
  3.         <Name>Kirin</Name>
  4.         <Age>31</Age>
  5.         <Location>Beijing</Location>
  6.     </Person>
  7.     <Person>
  8.         <Name>Jade</Name>
  9.         <Age>30</Age>
  10.         <Location/>
  11.     </Person>
  12. </dataset>
复制代码

null用空元素来表示。

将数据库导出到XML文件
我们可以手写XML来准备数据,也可以从数据库中导出现有的数据,用FlatXmlDataSet.write()静态方法
即可,例如:

  1. QueryDataSet dataSet = new QueryDataSet(connection);
  2. dataSet.addTable(TABLE_NAME, "select * from " + TABLE_NAME);
  3. dataSet.addTable(...);
  4. FlatXmlDataSet.write(dataSet, new FileOutputStream("data.xml"));
  5. 重写getDataSet
  6. 有了文件数据,我们就需要重写getDataSet(),让它加载文件中的数据并返回。

  7. @Override
  8. protected IDataSet getDataSet() throws Exception {
  9.     // set column sensing as true, so it can dynamically and columns with null value.
  10.     return new FlatXmlDataSetBuilder()                  
  11.                 .setColumnSensing(true)
  12.                 .build(new FileInputStream("data.xml"));
  13. }
  14. IDatabaseTester
  15. DBTestCase重写了getConnection(),并把它设置为final,将获取connection的操作委托给IDatabaseTester,我们可以通过重写getDatabaseTester()方法来设置具体的IDatabaseTester。Dbunit中,IDatabaseTester的实现类一共有四个:

  16. DefaultDatabaseTester
  17. JdbcDatabaseTester
  18. DataSourceDatabaseTester
  19. JndiDatabaseTester
复制代码

它们的用途不言自明。

DatabaseTestCase重写了TestCase里的setUp()和tearDown()方法。

  1. protected void setUp() throws Exception
  2. {
  3.     super.setUp();
  4.     final IDatabaseTester databaseTester = getDatabaseTester();
  5.     assertNotNull( "DatabaseTester is not set", databaseTester );
  6.     databaseTester.setSetUpOperation( getSetUpOperation() );
  7.     databaseTester.setDataSet( getDataSet() );
  8.     databaseTester.setOperationListener(getOperationListener());
  9.     databaseTester.onSetup();
  10. }

  11. protected void tearDown() throws Exception
  12. {
  13.     try {
  14.         final IDatabaseTester databaseTester = getDatabaseTester();
  15.         assertNotNull( "DatabaseTester is not set", databaseTester );
  16.         databaseTester.setTearDownOperation( getTearDownOperation() );
  17.         databaseTester.setDataSet( getDataSet() );
  18.         databaseTester.setOperationListener(getOperationListener());
  19.         databaseTester.onTearDown();
  20.     } finally {
  21.         tester = null;
  22.         super.tearDown();
  23.     }
  24. }
复制代码

可以看出它们的大体意图:为tester设置操作、数据集和监听器,然后执行相应的操作。获取数据集的
是抽象方法,需要我们来实现。监听器主要负责在得到数据连接或setUp、tearDown结束后执行的操作,
使用默认实现即可。我们主要来说说getSetUpOperation和getTearDownOperation返回的DatabaseOper
ation。

DatabaseOperation
DatabaseOperation定义了对数据库进行的操作,它是一个抽象类,通过静态字段提供了几种内置的实现:

NONE:不执行任何操作,是getTearDownOperation的默认返回值。
UPDATE:将数据集中的内容更新到数据库中。它假设数据库中已经有对应的记录,否则将失败。
INSERT:将数据集中的内容插入到数据库中。它假设数据库中没有对应的记录,否则将失败。
REFRESH:将数据集中的内容刷新到数据库中。如果数据库有对应的记录,则更新,没有则插入。
DELETE:删除数据库中与数据集对应的记录。
DELETE_ALL:删除表中所有的记录,如果没有对应的表,则不受影响。
TRUNCATE_TABLE:与DELETE_ALL类似,更轻量级,不能rollback。
CLEAN_INSERT:是一个组合操作,是DELETE_ALL和INSERT的组合。是getSetUpOeration的默认返回值。
由此我们可以总结出,在一个测试执行前后,DbUnit会为我们做哪些工作:

移除数据库中的所有记录(CLEAN_INSERT中的DELETE_ALL)。
将数据集中的数据加载到数据库中(CLEAN_INSERT中的INSERT)。
运行测试。
测试运行完毕后,不执行任何操作。
我们可以根据需要,在测试类中重写setUp和tearDown,以实现定制的需求。比如,数据库中已经有一
些数据,我们不希望数据集中的数据对它们产生任何影响,这时可以先将数据库中的数据备份到内存中,
等测试完成后再恢复到数据库中,代码如下:

  1. private IDataSet dataSetBackup;
  2. private static final String[] TABLE_NAMES = new String[] { "..." };

  3. @Override
  4. protected void setUp() throws Exception {
  5.     dataSetBackup = new CachedDataSet(getConnection().createDataSet(TABLE_NAMES));
  6.     super.setUp();      
  7. }

  8. @Override
  9. protected void tearDown() throws Exception {
  10.     try {
  11.         final IDatabaseTester databaseTester = getDatabaseTester();
  12.         assertNotNull( "DatabaseTester is not set", databaseTester );
  13.         databaseTester.setTearDownOperation( getTearDownOperation() );
  14.         databaseTester.setDataSet( dataSetBackup ); // 这里不使用getDataSet(),而是使用备份的数据库中数据
  15.         databaseTester.setOperationListener(getOperationListener());
  16.         databaseTester.onTearDown();
  17.     } finally {
  18.         tester = null;
  19.         dataSetBackup = null;
  20.         //super.tearDown(); // 这里不再调用基类的tearDown
  21.     }
  22. }

  23. @Override
  24. protected DatabaseOperation getTearDownOperation() {
  25.     return DatabaseOperation.CLEAN_INSERT;
  26. }
复制代码

测试前用CLEAN_INSERT,是用数据集覆盖数据库,测试后用CLEAN_INSERT,使用备份的数据库覆盖之
前插入到数据库中的数据集。

完整的基类代码在这里。

好了,现在可以开始测试了。

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-23 16:44 , Processed in 0.073883 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表