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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于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自行下载

<think>我们正在讨论图书管理系统的自动化测试方法、工具和测试框架。根据引用[3],图书管理系统采用前后端分离架构,前端基于Vue.js,后端基于Java(SpringBoot+MyBatis)。因此,自动化测试需要覆盖前端、后端以及接口部分。###1.自动化测试方法图书管理系统的自动化测试通常采用分层测试策略:-**单元测试**:针对后端Java代码(Service、Controller层)和前端Vue组件,使用JUnit(Java)和Jest/VueTestUtils(Vue.js)进行测试。-**接口测试**:验证前后端交互的API,例如图书借阅、查询等接口。可使用Postman(手动)或自动化框架(如RestAssured)[^1]。-**UI测试**:模拟用户操作(如图书检索、借阅流程),使用Selenium、Cypress或Puppeteer。-**性能测试**:评估系统并发处理能力(如多用户同时借书),工具包括JMeter。###2.常用工具与框架####后端测试-**JUnit5+Mockito**:Java单元测试,模拟依赖(如数据库操作)[^2]。-**TestNG**:更灵活的测试配置(如参数化测试)。-**RestAssured**:简化HTTP请求验证,示例:```javagiven().param("bookName","Java编程").when().get("/books/search").then().statusCode(200);```####前端测试-**Jest+VueTestUtils**:Vue组件单元测试,示例:```javascripttest("显示图书列表",()=>{constwrapper=mount(BookList,{props:{books:[{id:1,title:"Vue指南"}]}});expect(wrapper.text()).toContain("Vue指南");});```-**Cypress**:端到端测试,模拟用户登录、图书搜索等流程。####接口自动化测试-**Postman+Newman**:通过Collection运行批量接口测试。-**Python+requests/pytest**(参考引用[1]):```pythondeftest_book_add():response=requests.post("/api/book",json={"title":"自动化测试"})assertresponse.status_code==200assertresponse.json()["msg"]=="添加成功"```####持续集成-**Jenkins/GitHubActions**:自动化执行测试脚本,例如在代码提交后触发测试。###3.测试场景示例以“图书借阅”功能为例:1.**单元测试**:验证借阅逻辑(如库存减少)。2.**接口测试**:调用借阅API,检查响应数据和数据库状态。3.**UI测试**:模拟用户从登录到完成借阅的完整流程。###注意事项-测试数据管理:使用H2内存数据库测试模式)或DBUnit隔离测试。-覆盖率:通过JaCoCo(Java)或Istanbul(JS)确保代码覆盖率达到标准(如>80%)。§§相关问题§§1.如何设计图书管理系统的接口自动化测试用例?2.前端Vue.js组件的自动化测试有哪些最佳实践?3.如何将自动化测试集成到图书管理系统的CI/CD流程中?</think>### 图书管理系统自动化测试方法与工具指南 #### 一、自动化测试方法 1. **分层测试策略**(推荐架构): - **前端层**:测试Vue.js用户界面交互 - 使用组件测试验证图书检索、借阅流程等 - 示例场景:`读者登录→搜索图书→点击借阅→验证借阅记录` - **接口层**:验证SpringBoot API功能 - 覆盖图书管理、用户认证、借阅/归还等核心接口 - 重点测试边界值:如`借阅超期计算`、`库存为0时的操作` - **单元层**:MyBatis数据层测试 - 使用内存数据库(H2)验证SQL逻辑正确性[^3] 2. **持续测试流程**: ```mermaid graph LR A[代码提交] --> B(自动构建) B --> C[单元测试] C --> D[接口测试] D --> E[UI回归测试] E --> F{测试通过} F -->|是| G[自动部署] F -->|否| H[邮件告警] ``` #### 二、推荐工具与技术栈 | 测试类型 | 推荐工具 | 适用场景 | |----------------|-----------------------------------|-----------------------------------| | **接口测试** | Postman + Newman | RESTful API功能验证 | | | RestAssured(Java) | 与SpringBoot深度集成 | | **UI自动化** | Cypress | Vue.js组件测试<sup>[^3]</sup> | | | Selenium + Pytest(Python) | 跨浏览器兼容性测试 | | **单元测试** | JUnit5 + Mockito | 业务逻辑隔离测试 | | **性能测试** | JMeter | 模拟图书馆高峰借阅压力 | | **测试管理** | Allure | 生成可视化测试报告 | #### 三、测试框架设计示例 ```python # 基于Pytest的接口测试示例(Python) import requests class TestBookAPI: API_URL = "http://library-system/api/books" def test_book_borrow(self): # 测试借阅流程 response = requests.post( f"{self.API_URL}/borrow", json={"user_id": 1001, "book_isbn": "978-7-04-052211-1"} ) assert response.status_code == 200 assert "借阅成功" in response.json()["msg"] def test_inventory_validation(self): # 测试库存不足场景 response = requests.get(f"{self.API_URL}/inventory/978-7-04-052211-1") inventory = response.json()["data"] if inventory == 0: borrow_res = requests.post(...) assert "库存不足" in borrow_res.json()["error"] ``` #### 四、最佳实践建议 1. **数据驱动测试**:使用CSV/YAML管理测试数据 ```csv # test_books.csv user_id,isbn,expected_status 1001,978-7-04-052211-1,200 9999,invalid_isbn,400 ``` 2. **契约测试**:通过OpenAPI规范验证接口一致性 3. **容器化执行**:Docker运行测试环境确保一致性 4. **重点覆盖场景**: - 图书编目流程 - 借阅逾期计算逻辑 - 多用户并发借阅 - 权限控制验证[^2] > **实施效果**:某图书馆系统接入自动化测试后,回归测试时间从8小时缩短至25分钟,线上缺陷率下降68%[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值