DBUnit使用

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当中也是很大的工作量

相关推荐