每次执行@Test方法前都执行一次DB初始化(SpringBoot Test + JUnit5环境)

news2024/12/24 17:20:34

引言

在执行单元测试时,可以使用诸如H2内存数据库替代线上的Mysql数据库等,如此在执行单元测试时就能尽可能模拟真实环境的SQL执行,同时也无需依赖线上数据库,增加了测试用例执行环境的可移植性。而使用H2数据库时,通常会在执行单元测试前先初始化数据库,即执行SQL脚本来对H2内存数据库进行初始化。
例如可通过如下配置指定H2的初始化脚本:

注: 如下配置中的
spring.sql.init.schema-locations
spring.sql.init.data-locations
即对应数据库schema(table定义)、data(数据插入)的导入脚本。

spring:
  # Sql初始化配置
  sql:
    init:
      # 导入h2 table定义
      schema-locations: classpath:h2/demo-schema.sql
      # 导入h2 数据定义
      data-locations: classpath:h2/demo-data.sql
  # 数据库配置
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    # ============================================================
    # ============= 使用H2内存数据库 ================================
    # ============================================================
    driver-class-name: org.h2.Driver
    # 使用h2内存数据(以mysql兼容模式运行)
    url: jdbc:h2:mem:rbac;MODE=MySQL;DATABASE_TO_LOWER=TRUE
    username: root
    password: 123456

相关SQL脚本示例如下:

-- h2/demo-schema.sql定义
-- 注意create table前都先执行drop table脚本,如此便可保证Sql脚本可以被覆盖执行
DROP TABLE IF EXISTS category;
CREATE TABLE category (
	id bigint(20) NOT NULL COMMENT '主键ID',
	parent_category_id bigint(20) DEFAULT NULL COMMENT '上级分类ID',
	category_name varchar(64) NOT NULL COMMENT '分类名称',
	category_desc varchar(512) DEFAULT NULL COMMENT '分类描述',
	create_time datetime NOT NULL COMMENT '创建时间',
	update_time datetime DEFAULT NULL COMMENT '修改时间'
) ENGINE = InnoDB;


-- ------------------------------------------------------------------------
-- h2/demo-data.sql定义
-- 注意先执行schema定义sql,再执行data定义sql,多个sql脚本需考虑好先后执行顺序
INSERT INTO category
(id,   parent_category_id, category_name, category_desc,      create_time,                update_time) VALUES
( 1,   null,               '分类1',       '查询测试专用数据',    '2023-01-01T15:58:04.105',   '2023-01-07T15:58:04.105'),
( 11,   1,                 '分类1-1',     '分类1-1描述',        '2023-01-02T15:58:04.105',   '2023-01-07T15:58:04.105');

但问题是如上SQL初始化配置会在Spring环境启动时仅执行一次,后续的单元测试都是作用在这同一套脚本的基础之上,不同的单元测试都对这套基础数据进行修改之后,可能会导致数据混乱,进而导致单元测试执行失败。

比较理想的状态就是在每个单元测试方法执行之前,都执行一遍数据库初始化,如此便能保证每个单元测试方法执行前的数据库数据都是固定的,不受其他单元测试的影响,如此在编写单元测试时都以统一的基础数据为基准,无需考虑前后单元测试间的数据依赖,减轻了单元测试方法的开发难度。

接下来本文主要介绍两种在每次单元测试方法执行前都会通过SQL脚本对数据库进行始化的方式。

方式1: @Sql

可在单元测试类 或者 单元测试方法上使用@Sql注解,

import org.springframework.test.context.jdbc.Sql;

@Sql(
        //SQL初始化脚本(会按照数组声明顺序依次执行Sql脚本)
        scripts = {
                "classpath:h2/schema.sql",
                "classpath:h2/data.sql"
        },
        //SQL初始化执行阶段(BEFORE_TEST_METHOD: 测试方法执行前, AFTER_TEST_METHOD: 测试方法执行后)
        executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
@SpringBootTest
public class MyBizTest {
	
	//具体方法上也可使用@Sql注解,此处@Sql定义会覆盖类上的@Sql
	//可通过在测试方法 或 测试类上 使用@SqlMergeMode(MergeMode.OVERRIDE|MERGE)来设置方法的@Sql是覆盖、还是合并类上的@Sql
	@Test
	void testMyBiz() {
		//......
	}
}

如果定义了基础测试类,也可将@Sql直接定义在基础测试类上:

import org.springframework.test.context.jdbc.Sql;

@Sql(
        //SQL初始化脚本(会按照数组声明顺序依次执行Sql脚本)
        scripts = {
                "classpath:rbac-h2/schema.sql",
                "classpath:rbac-h2/data.sql"
        },
        //SQL初始化执行阶段(BEFORE_TEST_METHOD: 方法执行前, AFTER_TEST_METHOD: 方法执行后)
        executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
@SpringBootTest
public class BaseTest {
	//......
}

---

public class MyBizTest extends BaseTest {
	
	@Test
	void testMyBiz() {
		//......
	}
}

对@Sql的支持是由org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener提供的,并且它是默认启用的。
在这里插入图片描述
关于@Sql、@SqlConfig、@SqlMergeMode、@SqlGroup的更多使用可参见:
https://docs.spring.io/spring-framework/docs/5.3.29/reference/html/testing.html#testcontext-executing-sql-declaratively

方式2:ResourceDatabasePopulator

除了通过注解的方式,也可以通过编程的方式执行数据库初始脚本。Spring提供了如下执行SQL脚本的工具类:

  • org.springframework.jdbc.datasource.init.ScriptUtils
  • org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
  • org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
  • org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests

ScriptUtils提供了一组用于处理SQL脚本的静态实用程序方法,主要用于框架内部使用。然而,如果您需要完全控制SQL脚本的解析和运行方式,ScriptUtils可能比稍后介绍的其他一些替代方案更适合您的需求。更多细节请参考ScriptUtils中各个方法的javadoc。

ResourceDatabasePopulator提供了一个基于对象的API,通过使用在外部资源中定义的SQL脚本以编程方式填充、初始化或清理数据库。ResourceDatabasePopulator提供了配置在解析和运行脚本时使用的字符编码、语句分隔符、注释分隔符和错误处理标志的选项。每个配置选项都有一个合理的默认值。有关默认值的详细信息,请参阅javadoc。要运行在ResourceDatabasePopulator中配置的脚本,您可以调用populate(Connection)方法对java.sql.Connection运行populator,或者调用execute(DataSource)方法对javax.sql.DataSource运行populator。

ResourceDatabasePopulator在内部委托ScriptUtils解析和运行SQL脚本。类似地,AbstractTransactionalJUnit4SpringContextTests和AbstractTransactionalTestNGSpringContextTests中的executeSqlScript(…)方法在内部使用ResourceDatabasePopulator来运行SQL脚本。

如下使用ResourceDatabasePopulator ,结合@BeforeEach在每个@Test方法执行前对数据库进行初始化:

@SpringBootTest
public class MyBizTest {

	//注入数据源
	@Resource
    private DataSource dataSource;
    
    
    //@BeforeEach即对应没个@Test方法执行前,
    //亦可通过@BeforeAll指定在每个测试类执行前执行(需在测试类上标注@TestInstance(TestInstance.Lifecycle.PER_CLASS))
    @BeforeEach 
    public void beforeEachTestMethod() {
        //声明ResourceDatabasePopulator 
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        //加载SQL脚本,此处直接使用ClassPathResource, 所以具体path无需以classpath:开头
        //会按照数组声明顺序依次执行Sql脚本
        ClassPathResource[] scriptResources = Stream.of(
                "h2/schema.sql",
                "h2/data.sql"
        ).map(ClassPathResource::new).toArray(ClassPathResource[]::new);
        //添加SQL脚本
        populator.addScripts(scriptResources);
        //使用数据源执行SQL脚本
        populator.execute(this.dataSource);
    }

	@Test
	void testMyBiz() {
		//......
	}    
}

如果定义了基础测试类,也可将@BeforeEach逻辑直接定义在基础测试类上:

@SpringBootTest
public class BaseTest {

	//注入数据源
	@Resource
    private DataSource dataSource;    
    
    //@BeforeEach即对应没个@Test方法执行前,
    //亦可通过@BeforeAll指定在每个测试类执行前执行(需在测试类上标注@TestInstance(TestInstance.Lifecycle.PER_CLASS))
    @BeforeEach 
    public void beforeEachTestMethod() {
        //声明ResourceDatabasePopulator 
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        //加载SQL脚本,此处直接使用ClassPathResource, 所以具体path无需以classpath:开头
        //会按照数组声明顺序依次执行Sql脚本
        ClassPathResource[] scriptResources = Stream.of(
                "h2/schema.sql",
                "h2/data.sql"
        ).map(ClassPathResource::new).toArray(ClassPathResource[]::new);
        //添加SQL脚本
        populator.addScripts(scriptResources);
        //使用数据源执行SQL脚本
        populator.execute(this.dataSource);
    }
}

---

public class MyBizTest extends BaseTest {
	
	@Test
	void testMyBiz() {
		//......
	}
}

参考:
https://docs.spring.io/spring-framework/docs/5.3.29/reference/html/testing.html#testcontext-executing-sql

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/850285.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node.js |(二)Node.js API:fs模块 | 尚硅谷2023版Node.js零基础视频教程

学习视频:尚硅谷2023版Node.js零基础视频教程,nodejs新手到高手 文章目录 📚文件写入🐇writeFile 异步写入🐇writeFileSync 同步写入🐇appendFile / appendFileSync 追加写入🐇createWriteStrea…

点成分享丨qPCR仪的原理与使用——以Novacyt产品为例

近年来,PCR检测在多种领域发挥着巨大的作用。短时高效和即时监测都成为了PCR仪发展的方向。作为世界领先的制造商之一,Novacyt公司为来自全球多个国家和行业的用户提供了优质的qPCR仪。 MyGo Mini S qPCR仪是一种紧凑型的实时qPCR仪,非常适合…

【算法|双指针系列No.1】leetcode283. 移动零

个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…

c++进阶--二叉搜索树

目录 前言 一、二叉搜索树 1.二叉搜索树概念 2.二叉搜索树操作 二、二叉搜索树实现 0.定义一个节点 1.定义一棵树 2.增删改查 2.1.查找 2.2.插入 2.3.删除 2.3.1非递归删除法 a.只有左孩子 -- 删除14 b.只有右孩子-- 删除10 c.有左右孩子--删除8 2.3.2递归删除…

【论文总结】Chatting with GPT-3 for Zero-Shot Human-Like Mobile Automated GUI Testing

与GPT-3对话进行零尝试人类化移动自动化GUI测试 摘要: 移动应用在人们的日常生活中变得不可或缺,而自动化图形用户界面(Graphical User Interface,GUI)测试广泛用于应用程序质量保证。对于自动化GUI测试,越…

rv1126更新rknpu驱动教学

测试平台:易佰纳rv1126 38板 查看板端版本-------------------------------------------------- 1:查看npu驱动版本 dmesg | grep -i galcore,可以看到版本为6.4.3.5 2:查看rknn-server版本 strings /usr/bin/rknn_server | g…

新能源电动车充电桩控制主板的材料选择

新能源电动车充电桩控制主板的材料选择 你是否想过,汽车充电桩控制主板的材料选择竟然还有这么多讲究?不再只是简单的充电问题,而是涉及到耐高温、耐高压、耐腐蚀、耐潮湿、耐油污、低热耗等方方面面。 首先,安全性是材料选择的首要考虑因素…

mysql自增主键不连续情况分析

1.唯一键冲突 比如increnment_test中已经存在了co1为3的记录,当再插入col1为3的记录时,就会出现主键不唯一错误,但此时自增主键已经1,所以会发生主键不连续情况 DROP TABLE IF EXISTS increnment_test; CREATE TABLE increnment_test (id int(0) NOT NULL AUTO_INCREMENT,col…

svg使用技巧

什么是svg SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小&#xf…

Python-OpenCV中的图像处理

Python-OpenCV中的图像处理 颜色空间转换物体跟踪获取HSV的值几何变换图像缩放图像平移图像旋转仿射变换透视变换 图像阈值单阈值自适应阈值Otsus二值化 颜色空间转换 在 OpenCV 中有超过 150 中进行颜色空间转换的方法。但是你以后就会 发现我们经常用到的也就两种&#xff1…

ZMQ发布订阅模式二次封装

ZeroMQ 参考ZMQ从入门到掌握一 ZeroMQ是一种基于消息队列的多线程网络库,其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字。ZeroMQ 并不是一个对 socket 的封装,不能用它去实现已有的网络协议。它有…

加密、解密、编码

urlencode urlencode_百度百科 Base64是一种二进制到文本的编码方式,而且编码出的字符串只包含ASCII基础字符 下图是Base64码表,可以看到从0到63的每个数字都对应一个上面的一个字符。 文件-base64字符串互转 sun.misc包中的类 try (FileOutputStre…

Node.Js安装与配置教程

目录 1.下载官网 2.选择安装路径 3.添加环境变量 4.验证是否安装成功 5.修改模块下载位置 (1)查看npm默认存放位置 6.在node.js安装目录下,创建两个文件夹 7.修改默认文件夹 8.测试默认位置是否更改成功 9.安装报错解决办法 10.路径未更改成功解决办法 …

MIT 6.830数据库系统 -- lab six

MIT 6.830数据库系统 -- lab six 项目拉取引言steal/no-force策略redo log与undo log日志格式和检查点 开始回滚练习1:LogFile.rollback() 恢复练习2:LogFile.recover() 测试结果疑问点分析 项目拉取 原项目使用ant进行项目构建,我已经更改为…

【uniapp 小程序开发页面篇】代码编写规范 | 页面编写规范 | 小程序API

博主:_LJaXi Or 東方幻想郷 专栏: uni-app | 小程序开发 开发工具:HBuilderX 小程序开发页面篇 小程序组件规范小程序介绍小程序规范代码编写规范须遵循的开发规范 运行特性编译器选择编译规则工程目录结构static目录 使用注意static目录 条件…

Spring-2-透彻理解Spring 注解方式创建Bean--IOC

今日目标 学习使用XML配置第三方Bean 掌握纯注解开发定义Bean对象 掌握纯注解开发IOC模式 1. 第三方资源配置管理 说明:以管理DataSource连接池对象为例讲解第三方资源配置管理 1.1 XML管理Druid连接池(第三方Bean)对象【重点】 数据库准备 -- 创建数据库 create …

Easys Excel的表格导入(读)导出(写)-----java

一,EasyExcel官网: 可以学习一些新知识: EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 二,为什么要使用easyexcle excel的一些优点和缺点 java解析excel的框架有很多 : poi jxl,存在问题:非常的消耗内存, easyexcel 我们…

使用TDOSCommand调用Powershell脚本对进程进行操作

列出当前运行的进程: varPowerShellPath, ScriptPath, CommandLine: string; beginMemo6.Clear;PowerShellPath : powershell.exe ; // 假设 PowerShell 可执行文件在系统环境变量中// 构造命令行参数CommandLine : Get-Process | Select-Object Name,Id;// 设置命…

【Linux】总结2-进程篇1

文章目录 冯诺伊曼结构操作系统什么是程序?什么是进程?操作系统是如何来管理进程的?PCB(struct task_struct{...}) 冯诺伊曼结构 冯诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行…

Stable Diffusion - 常用的负向提示 Embeddings 解析与 坐姿 (Sitting) 提示词

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/132145248 负向 Embeddings 是用于提高 StableDiffusion 生成图像质量的技术,可以避免生成一些不符合预期的图像特征,比如…