DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果。
虽然不是什么新鲜货,但最近正好用到,就把学到的跟大家分享一下。
关键词:数据库层测试,DAO层测试,DBUnit教程,DBUnit入门,DBUnit实例,Sring中结合DBUnit对Dao层测试
目录
简介
前提条件
Maven配置
准备工作
实例详解
测试基类
关于数据集
Example 1 FlatXmlDataSet
Example 2 ReplacementDataSet
Example 3 XlsDataSet
Example 4 QueryDataSet
Example 5 other
Troubleshooting
参考
简介
DBunit通过维护真实数据库与数据集(IDataSet)之间的关系来发现与暴露测试过程中的问题。IDataSet 代表一个或多个表的数据。此处IDataSet可以自建,可以由数据库导出,并以多种方式体现,xml文件、XLS文件和数据库查询数据等。
基于DBUnit 的测试的主要接口是IDataSet,可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet 实现为:
FlatXmlDataSet :数据的简单平面文件 XML 表示
QueryDataSet :用 SQL 查询获得的数据
DatabaseDataSet :数据库表本身内容的一种表示
XlsDataSet :数据的excel 表示
前提条件
- JDK 1.7
- Maven 3
Maven配置
pom里添加以下的dependencies
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.1</version>
</dependency>
实例详解
测试流程大概是这样的,建立数据库连接-> 备份表 -> 调用Dao层接口 -> 从数据库取实际结果-> 事先准备的期望结果 -> 断言 -> 回滚数据库 -> 关闭数据库连接
因为每个测试都有很多共性,所以提取成抽象基类如下。
测试基类:
package com.demo.test.dao.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.DefaultDataSet;
import org.dbunit.dataset.DefaultTable;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
/**
* @Description: BaseDaoTest class
* @author wadexu
*
* @updateUser
* @updateDate
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/test/resources/mvc-dispatcher-servlet.xml")
@TransactionConfiguration(defaultRollback = true)
public abstract class BaseDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private DataSource dataSource;
private static IDatabaseConnection conn;
private File tempFile;
public static final String ROOT_URL = System.getProperty("user.dir") + "/src/test/resources/";
@Before
public void setup() throws Exception {
//get DataBaseSourceConnection
conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
//config database as MySql
DatabaseConfig dbConfig = conn.getConfig();
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
}
@After
public void teardown() throws Exception {
if (conn != null) {
conn.close();
}
}
/**
*
* @Title: getXmlDataSet
* @param name
* @return
* @throws DataSetException
* @throws IOException
*/
protected IDataSet getXmlDataSet(String name) throws DataSetException, IOException {
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
builder.setColumnSensing(true);
return builder.build(new FileInputStream(new File(ROOT_URL + name)));
}
/**
* Get DB DataSet
*
* @Title: getDBDataSet
* @return
* @throws SQLException
*/
protected IDataSet getDBDataSet() throws SQLException {
return conn.createDataSet();
}
/**
* Get Query DataSet
*
* @Title: getQueryDataSet
* @return
* @throws SQLException
*/
protected QueryDataSet getQueryDataSet() throws SQLException {
return new QueryDataSet(conn);
}
/**
* Get Excel DataSet
*
* @Title: getXlsDataSet
* @param name
* @return
* @throws SQLException
* @throws DataSetException
* @throws IOException
*/
protected XlsDataSet getXlsDataSet(String name) throws SQLException, DataSetException,
IOException {
InputStream is = new FileInputStream(new File(ROOT_URL + name));
return new XlsDataSet(is);
}
/**
* backup the whole DB
*
* @Title: backupAll
* @throws Exception
*/
protected void backupAll() throws Exception {
// create DataSet from database.
IDataSet ds = conn.createDataSet();
// create temp file
tempFile = File.createTempFile("temp", "xml");
// write the content of database to temp file
FlatXmlDataSet.write(ds, new FileWriter(tempFile), "UTF-8");
}
/**
* back specified DB table
*
* @Title: backupCustom
* @param tableName
* @throws Exception
*/
protected void backupCustom(String... tableName) throws Exception {
// back up specific files
QueryDataSet qds = new QueryDataSet(conn);
for (String str : tableName) {
qds.addTable(str);
}
tempFile = File.createTempFile("temp", "xml");
FlatXmlDataSet.write(qds, new FileWriter(tempFile), "UTF-8");
}
/**
* rollback database
*
* @Title: rollback
* @throws Exception
*/
protected void rollback() throws Exception {
// get the temp file
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
builder.setColumnSensing(true);
IDataSet ds =builder.build(new FileInputStream(tempFile));
// recover database
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
}
/**
* Clear data of table
*
* @param tableName
* @throws Exception
*/
protected void clearTable(String tableName) throws Exception {
DefaultDataSet dataset = new DefaultDataSet();
dataset.addTable(new DefaultTable(tableName));
DatabaseOperation.DELETE_ALL.execute(conn, dataset);
}
/**
* verify Table is Empty
*
* @param tableName
* @throws DataSetException
* @throws SQLException
*/
protected void verifyTableEmpty(String tableName) throws DataSetException, SQLException {
Assert.assertEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
}
/**
* verify Table is not Empty
*
* @Title: verifyTableNotEmpty
* @param tableName
* @throws DataSetException
* @throws SQLException
*/
protected void verifyTableNotEmpty(String tableName) throws DataSetException, SQLException {
Assert.assertNotEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
}
/**
*
* @Title: createReplacementDataSet
* @param dataSet
* @return
*/
protected ReplacementDataSet createReplacementDataSet(IDataSet dataSet) {
ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet);
// Configure the replacement dataset to replace '[NULL]' strings with null.
replacementDataSet.addReplacementObject("[null]", null);
return replacementDataSet;
}
}
##转载注明出处: http://www.cnblogs.com/wade-xu/p/4547381.html
我这里介绍的测试案例都是基于Spring项目的,如果是普通的项目,如何配置数据库连接如下:
public static void init() throws Exception {
// get DataBaseSourceConnection
testDataSource = new BasicDataSource();
testDataSource.setDriverClassName("com.mysql.jdbc.Driver");
testDataSource.setUrl("jdbc:mysql://10.52.26.11:3306/Test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true");
testDataSource.setUsername("xxx");
testDataSource.setPassword("xxxxx");
connection = new DatabaseDataSourceConnection(testDataSource);
ImporterManager.setJdbcTemplate(new JdbcTemplate(testDataSource));
}
关于数据集
DBUnit可以把所有表的记录存在一个数据集中:既可以是数据库中的表,也可以是文件中的数据。我们在此用FlatXmlDataSet来讲述。
在FlatXmlDataSet对应的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,就会根据第一行展现出来的表结构,自动将别的行的列补齐。
顺便提一句,DBUnit中还存在另一种格式的数据集XmlDataSet,在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的表示方法如红色部分。
##转载注明出处: http://www.cnblogs.com/wade-xu/p/4547381.html
Example 1
关于FlatXmlDataSet
package com.demo.test.dao.impl;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.dbunit.Assertion;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.filter.DefaultColumnFilter;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.demo.test.dao.BundleDao;
import com.demo.test.dao.VersionDao;
import com.demo.test.entity.InfoEntity;
import com.demo.test.exception.DBException;/**
* @Description: BundleDaoImpl Test via DBUnit
* @author wadexu
*
* @updateUser
* @updateDate
*/
public class BundleDaoImplDBUnitTest_Demo extends BaseDaoTest {
@Autowired
private BundleDao bundleDao;
@Autowired
private VersionDao versionDao;private static final String TABLE_DOCUMENTS_MASTER = "Documents_Master";
private static final String TABLE_FILE_VERSION = "FILE_VERSION";
private static final String VERSION_VALUE = "11.0.3";
@Test
public void testInsertBundles_1() throws Exception {
backupCustom(TABLE_FILE_VERSION, TABLE_DOCUMENTS_MASTER);
bundleDao.deleteAll();
bundleDao.insertBundle(getBundles());
//get actual tableInfo from DB
IDataSet dbDataSet = getDBDataSet();
ITable dbTable = dbDataSet.getTable(TABLE_DOCUMENTS_MASTER);
//get expect Information from xml file
IDataSet xmlDataSet = getXmlDataSet("expect_documents_master.xml");
ITable xmlTable = xmlDataSet.getTable(TABLE_DOCUMENTS_MASTER);
//exclude some columns which don't want to compare result
dbTable = DefaultColumnFilter.excludedColumnsTable(dbTable, new String[]{"IndexId", "DM_VERSION_ID"});
xmlTable = DefaultColumnFilter.excludedColumnsTable(xmlTable, new String[]{"IndexId", "DM_VERSION_ID"});
Assertion.assertEquals(xmlTable, dbTable);
rollback();
}}
首先我备份了两张用到的表,当然也可以备份所有的表,基类都有写这些方法 (backupCustom, backupAll)
然后调用Dao层提供的方法,删除所有数据,接着插入Bundle数据, getBundles()是我的私有方法
来源:https://www.cnblogs.com/wade-xu/p/4547381.html