赞
踩
官方使用指南:http://dbunit.sourceforge.net/howto.html
参考资料:https://www.cnblogs.com/wade-xu/p/4547381.html
码云代码(在Spring Cloud配置中心项目基础上):https://gitee.com/wudiyong/ConfigServer.git
介绍
DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果。
DbUnit has the ability to export and import your database data to and from XML datasets. Since version 2.0, DbUnit can also work with very large datasets when used in streaming mode. DbUnit can also help you to verify that your database data match an expected set of values.
DBunit通过维护真实数据库与数据集(IDataSet)之间的关系来发现与暴露测试过程中的问题。IDataSet 代表一个或多个表的数据。此处IDataSet可以自建,可以由数据库导出,并以多种方式体现,xml文件、XLS文件和数据库查询数据等。
基于DBUnit 的测试的主要接口是IDataSet,可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet 实现为:
FlatXmlDataSet :数据的简单平面文件 XML 表示
QueryDataSet :用 SQL 查询获得的数据
DatabaseDataSet :数据库表本身内容的一种表示
XlsDataSet :数据的excel 表示
测试流程大概是这样的,建立数据库连接-> 备份表 -> 调用Dao层接口 -> 从数据库取实际结果-> 事先准备的期望结果 -> 断言 -> 回滚数据库 -> 关闭数据库连接
1、添加maven依赖
- <dependency>
- <groupId>org.dbunit</groupId>
- <artifactId>dbunit</artifactId>
- <version>2.5.4</version>
- <scope>test</scope>
- </dependency>
2、创建测试类
通常一个测试类对应程序中的一个类,这些测试类为了重用一些公用的方法,可以创建一个基类,如下:
BaseDaoTest.java
- package com.ConfigServer;
-
- 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.oracle.OracleDataTypeFactory;
- 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.junit4.SpringRunner;
- @RunWith(SpringRunner.class)
- public abstract class BaseDaoTest {
- @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 oracle
- DatabaseConfig dbConfig = conn.getConfig();
- dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());
- }
-
- @After
- public void teardown() throws Exception {
- if (conn != null) {
- conn.close();
- }
- }
-
- /**
- * 从xml文件中获取数据集,该文件保存的是用于断言的期望数据
- * 下一步可以通过ITable dbTable = xmlDataSet.getTable("表名")获取表结构和表数据,
- * FlatXmlDataSet用XML文件中该表的第一行数据来制定表的结构,所以,如果表的第一行
- * 有字段为空,则得到的表结构会与数据库表结构不一致,可能是字段顺序不同,也可以能
- * 是字段个数不同,因为xml文件中该表的后续的行会补充字段,如xml内容如下:
- * <dataset>
- * <USER_INFO ID="1257436A2B3F4D31B4DF657043C30175" AGE="23" PHONE="12345678921"/>
- * <USER_INFO ID="AB2470AACFCA4E0BB6AF03B713DFDB99" NAME="休息休息" AGE="24"/>
- * </dataset>
- * 则得到的表结构为ID,AGE,PHONE,NAME
- * 而实际上数据库的表结构为ID,NAME,AGE,PHONE,可能还有更多字段,因为这些字段值都为空
- * 从而导致断言失败,因为表结构不一致,解决方法:
- * 首先修改xml文件,把表的第一行字段为空的用"[null]"占位符补上:
- * <USER_INFO ID="1257436A2B3F4D31B4DF657043C30175" NAME="[null]" AGE="23" ADDR="[null]" PHONE="12345678921"/>
- * 然后把ITable xmlTable = xmlDataSet.getTable(TABLE_USER_INFO);改成如下方式:
- * ReplacementDataSet replacementDataSet = createReplacementDataSet(xmlDataSet);
- * ITable xmlTable = replacementDataSet.getTable(TABLE_USER_INFO);
- */
- 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)));
- }
-
- /**
- * 从数据库中获取数据集
- * 下一步可以通过ITable dbTable = dbDataSet.getTable("表名")获取表结构和表数据,
- * 这里直接从数据库获取表结构,所以,不管字段是否为空,表结构都与数据库一致
- */
- protected IDataSet getDBDataSet() throws SQLException {
- return conn.createDataSet();
- }
-
- /**
- * 从数据库获取查询数据集
- * 通过自己的query语句到数据库查询结果,代码如下:
- * QueryDataSet queryDataSet = getQueryDataSet();
- * queryDataSet.addTable("tableName or other String", "select * from yourTableName");
- * ITable dbTable = queryDataSet.getTable("tableName or other String");
- * 根据需要,该结果集可以作为期望值,也可以作为实际值
- * 作为期望值:比如,要测试一个查询方法,则可以把该结果与被测试的查询方法返回的结果做比较
- * 作为实际值:拿该结果与xml文件设置的期望值对比
- */
- protected QueryDataSet getQueryDataSet() throws SQLException {
- return new QueryDataSet(conn);
- }
-
- /**
- * 从Excel获取数据集
- * @param name Excel文件名
- */
- protected XlsDataSet getXlsDataSet(String name) throws SQLException, DataSetException,
- IOException {
- InputStream is = new FileInputStream(new File(ROOT_URL + name));
- return new XlsDataSet(is);
- }
-
- /**
- * 备份整个数据库
- */
- 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");
- }
-
- /**
- * 备份指定的表
- */
- 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");
- }
-
- /**
- * 回滚数据库
- * CLEAN_INSERT:DELETE_ALL和INSERT的组合,即先删除数据库的全部记录,然后再通过备份文件恢复;
- * UPDATE:用备份文件还原数据库中被修改的记录,前提是数据库表的数据要存在,如果缺少数据则会报错,
- * 如果有新增的数据,该数据不会被删掉;
- * 所以,用CLEAN_INSERT不管什么情况都可以回滚数据库,但如果有外键依赖会怎样?
- */
- 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);
- }
-
-
- /**
- * 清空指定表的数据,很少使用
- */
- protected void clearTable(String tableName) throws Exception {
- DefaultDataSet dataset = new DefaultDataSet();
- dataset.addTable(new DefaultTable(tableName));
- DatabaseOperation.DELETE_ALL.execute(conn, dataset);
- }
-
- /**
- * 验证指定的表是否为空
- */
- protected void verifyTableEmpty(String tableName) throws DataSetException, SQLException {
- Assert.assertEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
- }
-
- /**
- * 验证指定的表是否非空
- */
- protected void verifyTableNotEmpty(String tableName) throws DataSetException, SQLException {
- Assert.assertNotEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
- }
-
- /**
- * 如果xml文件中的数据集存在列的值为空,可以用一个"[null]"占位符代替,然后调用该方法把"[null]"替换成null
- */
- 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;
- }
-
- }
创建测试类
DBUnitTest.java
- package com.ConfigServer;
-
- import org.dbunit.Assertion;
- import org.dbunit.dataset.IDataSet;
- import org.dbunit.dataset.ITable;
- import org.dbunit.dataset.ReplacementDataSet;
- import org.dbunit.dataset.filter.DefaultColumnFilter;
- import org.junit.Test;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import com.ConfigServer.service.DBUnitTestService;
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
- public class DBUnitTest extends BaseDaoTest {
- @Autowired
- private DBUnitTestService dbUnitTestService;
-
- /**
- * 用户信息表表名
- */
- private static final String TABLE_USER_INFO = "USER_INFO";
-
- @Test
- public void updateUserInfo() throws Exception {
- try {
- //backup table from DB
- backupCustom(TABLE_USER_INFO);
- //被测试的方法
- dbUnitTestService.updateUserInfo("AB2470AACFCA4E0BB6AF03B713DFDB99", "休息休息");
-
- //get actual tableInfo from DB
- IDataSet dbDataSet = getDBDataSet();
- ITable dbTable = dbDataSet.getTable(TABLE_USER_INFO);
-
- //get expect Information from xml file
- IDataSet xmlDataSet = getXmlDataSet("expect_user_info.xml");
- ReplacementDataSet replacementDataSet = createReplacementDataSet(xmlDataSet);
- ITable xmlTable = replacementDataSet.getTable(TABLE_USER_INFO);
-
- //exclude some columns which don't want to compare result
- dbTable = DefaultColumnFilter.excludedColumnsTable(dbTable, new String[]{"ID"});
- xmlTable = DefaultColumnFilter.excludedColumnsTable(xmlTable, new String[]{"ID"});
-
- // Assert.assertEquals(dbTable.getRowCount(), xmlTable.getRowCount());//比较行数
- Assertion.assertEquals(xmlTable, dbTable);
- } catch (Exception e) {
- }finally{
- rollback();
- }
-
- }
-
- }
被测试的类DBUnitTestService.java:
- @Service
- public class DBUnitTestService {
- @Autowired
- private JdbcTemplate jdbcTemplate;
-
- public void updateUserInfo(String id,String name) {
- String sql = "update USER_INFO t set t.NAME=? where t.ID=?";
- jdbcTemplate.update(sql, name,id);
- }
- }
用做期望值的xml文件expect_user_info.xml
- <?xml version='1.0' encoding='UTF-8'?>
- <dataset>
- <USER_INFO ID="1257436A2B3F4D31B4DF657043C30175" NAME="[null]" AGE="23" ADDR="[null]" PHONE="12345678921"/>
- <USER_INFO ID="AB2470AACFCA4E0BB6AF03B713DFDB99" NAME="休息休息" AGE="24"/>
- </dataset>
数据库内容如下:
3、其它知识点
1)存在外键依赖的情况
如果操作的数据库表被其它表依赖,则备份的时候要先备份父表再备份子表(即使子表没有被修改),否则,回滚会报错(因为回滚是先清空表再通过备份文件写入),如学生表"STUDENT_INFO"依赖于用户表"USER_INFO",则备份顺序为:
backupCustom("USER_INFO","STUDENT_INFO");
// TODO 继续补充更多内容
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。