ymruse 2013-07-29
前言:本文章需要JUnit单元测试框架的基础知识,若读者还不具备,请阅读笔者的JUnit文章:http://ray-yui.iteye.com/blog/1914106
UnitTest系列文章:
使用JUnit开发单元测试:http://ray-yui.iteye.com/blog/1914106
使用EasyMock扩展Junithttp://ray-yui.iteye.com/blog/1916170
使用Cactus测试Servlethttp://ray-yui.iteye.com/blog/1917515
使用SpringTestContext测试Spring应用http://ray-yui.iteye.com/blog/1921424
使用Cobertura生成测试覆盖率报告http://ray-yui.iteye.com/blog/1921958
什么是DBUnit?
DBUnit是一个基于JUnit扩展的数据库测试框架。它能帮助我们更方便快捷的进行数据库测试.
为什么要使用DBUnit?
在我们使用JUnit单元测试框架编写单元测试的时候,少不免要对数据库进行操作,但请试想一下,当我要编写一个获取用户的单元测试时,数据库是不存在该记录的,那么我要测试获取用户时就需要往数据库添加一条用户记录,但当获取用户的单元测试完成并成功后,此测试并没有清理现场(删除插入数据库的记录),那样当我们再有单元测试需要插入记录时,就会造成ID冲突的情况,少量的单元测试还可以避免此种情况,但当单元测试的数据庞大时,就会出现各种各样的问题,而DBUnit可以帮助我们解决这类型的问题
DBUnit为我们做了什么?
DBUnit的设计理念是在测试之前,先对数据库进行备份,然后对数据库进行清空再插入我们为其准备的测试数据,最后在测试完毕后重新恢复数据库从而避免了JUnit对数据现场的破坏.
DBUnit使用:
1.首先为Maven增加DBUnit的依赖,还需要slf4j的依赖
<dependency> <groupId>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> <scope>compile</scope> </dependency>
在Maven管理的项目src/test/resource/下创建default-data.xml
<?xml version="1.0" encoding="UTF-8"?> <dataset> <!-- 注意,此处的user代表表名(database table name) --> <user id="1" username="123" password="456"/> <user id="2" username="123" password="456"/> <user id="3" username="123" password="456"/> <user id="4" username="123" password="456"/> <user id="5" username="123" password="456"/> </dataset>
以下代码为提取后的BaseTest
package com.accentrix.ray; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.InputStream; import javax.sql.DataSource; import org.dbunit.database.DatabaseDataSourceConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.database.QueryDataSet; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.dataset.xml.FlatXmlProducer; import org.dbunit.operation.DatabaseOperation; import org.junit.AfterClass; import org.junit.BeforeClass; import org.xml.sax.InputSource; public class BaseTest { public static IDatabaseConnection connection; public static DataSource dataSource; private File temp; @BeforeClass public static void init() throws Exception { // 通过DataSource获取DataBaseSourceConnection connection = new DatabaseDataSourceConnection(dataSource); } @AfterClass public static void destroy() throws Exception { if (connection != null) { connection.close(); } } protected IDataSet getDataSet(String name) throws DataSetException { // 通过类加载器获取default-data.xml文件的内容 InputStream is = this.getClass().getClassLoader() .getResourceAsStream(name + ".xml"); // IDataSet就类似是一个数据的容器,把default-data.xml内容 // 转换成了DBUnit可识别的数据返回出去 return new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is))); } protected void backupAll() throws Exception { // createDataSet代表从数据库中获取到DataSet,此时DataSet为数据库的内容 IDataSet ds = connection.createDataSet(); // 创建临时文件 temp = File.createTempFile("temp", "xml"); // 将数据库内容的DataSet写入临时文件当中 FlatXmlDataSet.write(ds, new FileWriter(temp), "UTF-8"); } protected void backupCustom(String... tableName) throws Exception { // 此种形式能保存某几张表的的数据,而不需要全部备份 QueryDataSet qds = new QueryDataSet(connection); for (String str : tableName) { qds.addTable(str); } temp = File.createTempFile("temp", "xml"); FlatXmlDataSet.write(qds, new FileWriter(temp), "UTF-8"); } protected void recover() throws Exception { // 获取数据库内容的临时文件生成DataSet IDataSet ds = new FlatXmlDataSet(new FlatXmlProducer(new InputSource( new FileInputStream(temp)))); // 使用DatabaseOperation的枚举方法先清空数据库内容再还原数据库 DatabaseOperation.CLEAN_INSERT.execute(connection, ds); } }
以下为单元测试类
package com.accentrix.ray; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.dbunit.dataset.IDataSet; import org.dbunit.operation.DatabaseOperation; import org.junit.Test; public class TestUser extends BaseTest { @Test public void testGetUser() throws Exception { // 首先获取到default-data.xml文件中的内容 IDataSet ds = getDataSet("default-data.xml"); // 调用backupAll方法保存数据库表 backupAll(); // 将default-data.xml中的内容替换成数据库的内容 DatabaseOperation.CLEAN_INSERT.execute(connection, ds); // 进行单元测试逻辑的判断 User user = userService.get(1); assertNotNull(user); assertEquals(user.getId(), new Integer(1)); assertEquals(user.getPassword(), "456"); assertEquals(user.getUsername(), "123"); // 最后进行数据库的恢复 // 当然我们也可以将backupAll和recover放在@Before和@After当中 recover(); } }
总结:
使用了DBUnit后可以实现了对数据库的隔离,成功弥补了JUnit单元测试不清理数据现场的缺憾,实际上DBUnit只是简单的在单元测试前把数据库的数据进行了备份然后插入xml中配置好的数据,在测试结束后再用备份好的原数据库数据填充回数据库.但当面对复杂的表关系和大数据量的时候,每次进行测试都进行数据的备份,也是一个很大的负担,而且把全部的测试数据都编写在xml当中也是很大的工作量