Springboot+DBunit单元测试,数据库隔离测试

本文详细介绍了如何在Spring Boot项目中使用DBunit进行数据库隔离的单元测试,包括配置步骤、测试数据准备、DBunit方法封装及业务代码测试实例。

关于DBunit

百度百科:dbunit是一个基于junit扩展的数据库测试框架。
简言之,dbunit是为了在单元测试中,通过备份数据库、导入测试数据、回滚恢复数据库的手段,在不污染数据库的前提下完成一系列单元测试工作。

DBunit关键知识点

IDataSet接口:用于操作表集合
ITable接口:用于操作表数据集合
DatabaseOperation类:对表数据执行一系列操作,比如刷新、删除、插入等

DBunit操作流程

在没使用DBunit做单元测试之前,一般遵循junit的测试流程:

  1. @BeforeClass在加载测试类之前执行初始化操作
  2. @Before执行单元测试方法前执行操作
  3. @Test执行测试逻辑
  4. @After执行单元测试方法后执行操作
  5. @AfterClass在测试类结束后操作

加入DBunit实际上只是在junit的流程中添加几步操作数据库的动作:

  1. @BeforeClass构建连接数据源(DataSource)
  2. @Before创建连接,备份数据并且插入测试数据
  3. @Test执行测试逻辑
  4. @After测试完成还原数据
  5. @AfterClass关闭连接

操作的流程不是固定的,具体还是要根据测试的逻辑来决定,如果能够加入事务特性进行单元测试可以大大精简流程

Springboot集成DBunit步骤

进入正题,这里使用的版本是:

  • DBunit 2.5.3
  • Springboot 2

一、引用依赖


		<!-- SpringBoot 测试 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!-- dbunit包 -->
		<dependency>
			<groupId>org.dbunit</groupId>
			<artifactId>dbunit</artifactId>
			<version>2.5.3</version>
			<scope>test</scope>
		</dependency>

二、初始化表&创建测试数据

初始化SQL脚本:

CREATE TABLE `db_unit_test` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

测试数据XML:

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <db_unit_test id="1" name="张三"/>
    <db_unit_test id="2" name="李四"/>
    <db_unit_test id="3" name="王五"/>
</dataset>

三、封装DBunit方法

AbstractBaseTest封装DBunit基本操作方法,包含初始化连接、关闭连接方法,备份数据、还原数据、导入数据和清空数据方法。其中AbstractBaseTest继承了AbstractTransactionalJUnit4SpringContextTests类的事务特性,可以方便使用事务回滚特性实现数据库0污染。
参考:https://www.cnblogs.com/wade-xu/p/4547381.html

/**
 *  <pre>
 * 单元测试基类
 * 封装DBunit相关操作方法
 * </pre>
 * https://www.cnblogs.com/wade-xu/p/4547381.html
 * @author oyf
 */
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
//继承AbstractTransactionalJUnit4SpringContextTests会在方法执行完成后进行事务回滚,如果需要不回滚事务需要在方法上加上 @Rollbak(false)
public abstract class AbstractBaseTest extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired
    private DataSource dataSource;
    /**
     * 数据库连接对象
     */
    private static IDatabaseConnection conn;
    /**
     * 备份文件
     */
    private File tempFile;
    /**
     * 文件跟目录
     */
    public static final String ROOT_URL = "src/test/resources/";

//    DBunit方法  --------------------------------------------------------

    /**
     * 获取数据库连接
     * @throws Exception
     */
    @Before
    public void setup() throws Exception {
        //get DataBaseSourceConnection
        conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
    }

    /**
     * 关闭数据库连接
     * @throws Exception
     */
    //这里不能用@After,当测试方法有@Rollback(false)注解时会在事务没有结束之前关闭了数据库连接
    @AfterTransaction
    public void teardown() throws Exception {
        if (conn != null) {
            conn.close();
        }
    }

    /**
     * Get Query DataSet
     *
     * @Title: getQueryDataSet
     * @return
     * @throws SQLException
     */
    protected QueryDataSet getQueryDataSet() throws SQLException {
        return new QueryDataSet(conn);
    }


    /**
     * 备份表数据
     *
     * @Title: backupCustom
     * @param tableName
     * @throws Exception
     */
    protected void backupCustom(String... tableName){
        try {
            // back up specific files
            QueryDataSet qds = getQueryDataSet();
            for (String str : tableName) {

                qds.addTable(str);
            }
            tempFile = new File(ROOT_URL+"temp.xml");
            FlatXmlDataSet.write(qds, new FileWriter(tempFile), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 清空表数据,并导入测试数据
     * @throws Exception
     */
    public void importTables(String file){
        try {
            IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File(ROOT_URL+file));
            DatabaseOperation.CLEAN_INSERT.execute(conn, dataSet);
        }  catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚数据
     *
     * @Title: rollback
     * @throws Exception
     */
    protected void rollback(){
        try {
            // 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);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 清空表数据
     *
     * @param tableName
     * @throws Exception
     */
    protected void clearTable(String tableName){
        try {
            DefaultDataSet dataset = new DefaultDataSet();
            dataset.addTable(new DefaultTable(tableName));
            DatabaseOperation.DELETE_ALL.execute(conn, dataset);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、准备业务代码

主要是准备查询数据用的service和dao,具体代码可以在https://gitee.com/oumuv/h2TestDemo下载
在这里插入图片描述

五、编写测试类

这里简单演示了两种方式实现数据库隔离测试,test1()方法用spring的事务特性,test2()使用DBunit手动回滚数据

/**
 * 测试demo
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = H2Application.class)
public class TestDemo2 extends AbstractBaseTest{

    @Autowired
    DBunitService dBunitService;

    /**
     * 使用事务回滚机制,自动还原数据库
     */
    @Test
    public void test1() {
        //导入测试数据
        importTables("dbunit-data.xml");
        List<DBunitEntity> list = dBunitService.list();
        Assert.assertNotNull(list);
    }

    /**
     * 加上注解 @Rollback(false) 不使用事务回滚,手动还原数据库
     */
    @Test
    @Rollback(false)
    public void test2() {
        //备份数据,备份数据在resources/temp.xml
        backupCustom("db_unit_test");
        //导入测试数据
        importTables("dbunit-data.xml");
        List<DBunitEntity> list = dBunitService.list();
        Assert.assertNotNull(list);
        //手动恢复数据
        rollback();
    }
}

运行测试用例发现查出来的数据确实是xml中的测试数据,而数据库中的数据并没有被污染也没有缺失,结果便是成功了!

具体代码可在https://gitee.com/oumuv/h2TestDemo自行下载

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值