SpringBoot教程&笔记|Demo08-整合DBUnit进行单元测试
本文主要讲解如何在springboot下整合DBUnit进行单元测试。
本教程在Demo08基础上添加DBUnit进行单元测试
官方使用指南:http://dbunit.sourceforge.net/howto.html
DBUnit介绍
DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果。
DBunit通过维护真实数据库与数据集(IDataSet)之间的关系来发现与暴露测试过程中的问题。IDataSet 代表一个或多个表的数据。此处IDataSet可以自建,可以由数据库导出,并以多种方式体现,xml文件、XLS文件和数据库查询数据等。
基于DBUnit 的测试的主要接口是IDataSet,可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet 实现为:
FlatXmlDataSet :数据的简单平面文件 XML 表示
QueryDataSet :用 SQL 查询获得的数据
DatabaseDataSet :数据库表本身内容的一种表示
XlsDataSet :数据的excel 表示
测试流程大概是这样的,建立数据库连接 -> 备份表 -> 清空数据表 -> 插入准备的数据 -> 调用Dao层接口 -> 从数据库取实际结果-> 事先准备的期望结果 -> 断言 -> 回滚数据库 -> 关闭数据库连接
添加依赖
引入 dbunit
、spring-boot-starter-log4j2
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.1.0.RC1</version>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.6.0</version>
</dependency>
数据准备
初始数据UserDriver.xml
,这里是dbunit
的FlatXmlDataSet
格式,元素名代表表名,属性对应字段名。
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user id="1" name="userOne" age="18" email="user-one@heardfate.com"/>
<user id="2" name="userTwo" age="20" email="userTwo@heardfate.com"/>
<user id="1060052237347897346" age="55" email="moreuser@heardfate.com"/>
</dataset>
期望数据UserDriver_check.xml
,期望数据必须映射所以字段,为空的用[NULL]
表示
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user id="1" name="userOne" age="18" email="user-one@heardfate.com"/>
<user id="2" name="userTwo" age="20" email="userTwo@heardfate.com"/>
<user id="1060052237347897346" name="[NULL]" age="55" email="moreuser@heardfate.com"/>
</dataset>
LOG4J2配置
配置log4j,打印日志,查看数据流转信息。
log4j.xml
内容:
<?xml version="1.0" encoding="UTF-8"?>
<!-- status=debug 可以查看log4j的装配过程 -->
<configuration status="off" monitorInterval="1800">
<properties>
<property name="LOG_HOME">/Volumes/MacDisk/logs/springbootdemo</property>
<property name="BACKUP_HOME">${LOG_HOME}/backup</property>
<property name="SERVER_NAME">demo04</property>
</properties>
<appenders>
<!-- 定义控制台输出 -->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-7level [%thread][%class{36}:%line] - %msg%n" />
</Console><!-- 程序员调试日志 -->
<RollingRandomAccessFile name="DevLog"
fileName="${LOG_HOME}/dev/${SERVER_NAME}.log" filePattern="${BACKUP_HOME}/${SERVER_NAME}.%d{yyyy-MM-dd}.log">
<PatternLayout
pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-7level [%thread][%class{36}:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingRandomAccessFile>
</appenders>
<Loggers>
<!-- 3rdparty Loggers -->
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
<!--只有com.heardfate.springboot.demo输出DEBUG日志-->
<Logger name="com.heardfate.springboot.demo" level="DEBUG">
<AppenderRef ref="DevLog" />
</Logger>
</Loggers>
</configuration>
编写DBUnit基类
在com/heardfate/springboot/demo/demo04/dbunit/BaseDBUnit.java
下添加DBUnit基类
package com.heardfate.springboot.demo.demo04.dbunit;
import org.dbunit.Assertion;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.*;
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.Assert;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.io.*;
import java.sql.SQLException;
/**
* @since: 2018/11/7
* @author: Mr.HeardFate
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional("transactionManager")
@Rollback
public class BaseDBUnit extends AbstractTransactionalJUnit4SpringContextTests {
protected static final Logger logger = LoggerFactory.getLogger(BaseDBUnit.class);
@Autowired
private DataSource dataSource;
private IDatabaseConnection conn;
private File tempFile;
public static final String ROOT_URL = System.getProperty("user.dir") + "/src/test/resources/";
@Before
public void setup() throws Exception {
logger.debug("Get DataBaseSourceConnection!");
// get DataBaseSourceConnection
conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
logger.debug("Config database as MySql");
// config database as MySql
DatabaseConfig dbConfig = conn.getConfig();
dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
}
/**
* @return
* @Title: getConnection
*/
protected IDatabaseConnection getConnection() {
return conn;
}
/**
* @param name
* @return
* @throws DataSetException
* @throws IOException
* @Title: getXmlDataSet
*/
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
*
* @return
* @throws SQLException
* @Title: getDBDataSet
*/
protected IDataSet getDBDataSet() throws SQLException {
return conn.createDataSet();
}
/**
* Get Query DataSet
*
* @return
* @Title: getQueryDataSet
*/
protected QueryDataSet getQueryDataSet() {
return new QueryDataSet(conn);
}
/**
* Get Excel DataSet
*
* @param name
* @return
* @throws DataSetException
* @throws IOException
* @Title: getXlsDataSet
*/
protected XlsDataSet getXlsDataSet(String name) throws DataSetException, IOException {
InputStream is = new FileInputStream(new File(ROOT_URL + name));
return new XlsDataSet(is);
}
/**
* backup the whole DB
*
* @throws Exception
* @Title: backupAll
*/
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
*
* @param tableName
* @throws Exception
* @Title: backupCustom
*/
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");
}
/**
* back specified DB table
*
* @param tableName
* @throws Exception
* @Title: backupCustomAud
*/
protected void backupCustomAud(String... tableName) throws Exception {
// back up specific files
QueryDataSet qds = new QueryDataSet(conn);
qds.addTable("revinfo");
for (String str : tableName) {
qds.addTable(str);
qds.addTable(str + "_aud");
}
tempFile = File.createTempFile("temp", "xml");
FlatXmlDataSet.write(qds, new FileWriter(tempFile), "UTF-8");
}
/**
* rollback database
*
* @throws Exception
* @Title: rollback
*/
protected void rollback() throws Exception {
logger.debug("rollback database Start!");
logger.debug("get the temp file");
// get the temp file
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
builder.setColumnSensing(true);
IDataSet ds = builder.build(new FileInputStream(tempFile));
logger.debug("recover database");
// recover database
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
logger.info("rollback database success!");
}
/**
* Clear data of table
*
* @param tableName
* @throws Exception
*/
protected void clearTable(String tableName) throws Exception {
logger.debug("clear table");
DefaultDataSet dataset = new DefaultDataSet();
dataset.addTable(new DefaultTable(tableName));
DatabaseOperation.DELETE_ALL.execute(conn, dataset);
}
/**
* Clear data of table And verify Table is Empty
*
* @param tableName
* @throws Exception
*/
protected void clearAndVerifyTable(String tableName) {
try {
clearTable(tableName);
verifyTableEmpty(tableName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* verify Table is Empty
*
* @param tableName
* @throws DataSetException
* @throws SQLException
*/
protected void verifyTableEmpty(String tableName) throws DataSetException, SQLException {
logger.debug("verify table is empty");
Assert.assertEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
}
/**
* verify Table is not Empty
*
* @param tableName
* @throws DataSetException
* @throws SQLException
* @Title: verifyTableNotEmpty
*/
protected void verifyTableNotEmpty(String tableName) throws DataSetException, SQLException {
logger.debug("verify table is not empty");
Assert.assertNotEquals(0, conn.createDataSet().getTable(tableName).getRowCount());
}
/**
* @param dataSet
* @return
* @Title: createReplacementDataSet
*/
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;
}
protected void verifyXmlDataWithCheckXml(String tableName, String checkDriverXml) {
try {
ITable dbTable = getDBDataSet().getTable(tableName);
logger.debug("dbTable {}",dbTable);
// 预期结果
IDataSet expectedDataSet = getXmlDataSet(checkDriverXml);
ReplacementDataSet replacementDataSet = createReplacementDataSet(expectedDataSet);
replacementDataSet.addReplacementObject("[NULL]", null);
ITable expectedTable = replacementDataSet.getTable(tableName);
logger.debug("expectedTable {}",expectedTable);
Assertion.assertEquals(dbTable, expectedTable);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
编写测试类UserDaoTest
在com/heardfate/springboot/demo/demo04/dao/UserDaoTest.java
下添加测试类
只测试类数据是否符合期望,插入新数据,获取所有数据
package com.heardfate.springboot.demo.demo04.dao;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.heardfate.springboot.demo.demo04.dbunit.BaseDBUnit;
import com.heardfate.springboot.demo.demo04.entity.User;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigInteger;
import java.util.List;
import static org.junit.Assert.*;
/**
* @since: 2018/11/7
* @author: Mr.HeardFate
*/
public class UserDaoTest extends BaseDBUnit {
@Autowired
private UserDao mapper;
private final String tableName = "user";
/**
* 清空数据插入准备的数据
*
* @throws Exception
*/
@Before
public void setUp() throws Exception {
try {
IDataSet dataSet = getXmlDataSet("UserDriver.xml");
DatabaseOperation.CLEAN_INSERT.execute(getConnection(), dataSet);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 检测XML数据插入是否符合预期
*
* @throws Exception
*/
@Test
public void check_xml_data() {
verifyXmlDataWithCheckXml(tableName, "UserDriver_check.xml");
}
@Test
public void insert() {
// 插入数据前先清空所有数据
clearAndVerifyTable(tableName);
User bean = new User();
bean.setName("testUser");
bean.setAge(20);
bean.setEmail("testUser@heardfate.com");
bean.setId(new BigInteger(IdWorker.getIdStr()));
logger.debug("{}:{}", bean.getClass().getName(), bean);
assertEquals("insert data error", 1, mapper.insert(bean));
logger.debug("{} id:{}", bean.getClass().getName(), bean.getId());
assertNotNull("Id is null", bean.getId());
}
@Test
public void deleteById() {
}
@Test
public void deleteByMap() {
}
@Test
public void delete() {
}
@Test
public void deleteBatchIds() {
}
@Test
public void updateById() {
}
@Test
public void update() {
}
@Test
public void selectById() {
}
@Test
public void selectBatchIds() {
}
@Test
public void selectByMap() {
}
@Test
public void selectOne() {
}
@Test
public void selectCount() {
}
@Test
public void selectList() {
List<User> list = mapper.selectList(null);
assertNotNull("Client list is null", list);
logger.debug("get page row count={}", list.size());
assertThat("select data size is zero", list.size(), Matchers.greaterThanOrEqualTo(1));
list.forEach((user) -> logger.debug("user inof: {}", user));
}
@Test
public void selectMaps() {
}
@Test
public void selectObjs() {
}
@Test
public void selectPage() {
}
@Test
public void selectMapsPage() {
}
}
运行测试方法
运行测试方法UserDaoTest
查看是否通过测试
测试全通过!