SpringBoot 教程 & 笔记 |Demo08- 整合 DBUnit 进行单元测试

本贴最后更新于 2336 天前,其中的信息可能已经沧海桑田

本文主要讲解如何在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 层接口 -> 从数据库取实际结果-> 事先准备的期望结果 -> 断言 -> 回滚数据库 -> 关闭数据库连接

添加依赖

引入 dbunitspring-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,这里是 dbunitFlatXmlDataSet 格式,元素名代表表名,属性对应字段名。

<?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>

log4j 配置

编写 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 查看是否通过测试
测试全通过!
运行测试

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    946 引用 • 1460 回帖 • 1 关注
  • dbunit
    1 引用
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 32 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...