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

引言

在执行单元测试时,可以使用诸如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=TRUEusername: rootpassword: 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@Testvoid 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 {@Testvoid 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 {//注入数据源@Resourceprivate 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);}@Testvoid testMyBiz() {//......}    
}

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

@SpringBootTest
public class BaseTest {//注入数据源@Resourceprivate 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 {@Testvoid testMyBiz() {//......}
}

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

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

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

相关文章

Oracle DB 安全性 : TDE HSM TCPS Wallet Imperva

• 配置口令文件以使用区分大小写的口令 • 对表空间进行加密 • 配置对网络服务的细粒度访问 TCPS 安全口令支持 Oracle Database 11g中的口令: • 区分大小写 • 包含更多的字符 • 使用更安全的散列算法 • 在散列算法中使用salt 用户名仍是Oracle 标识…

【JavaEE】Spring Boot - 日志文件

【JavaEE】Spring Boot 开发要点总结(3) 文章目录 【JavaEE】Spring Boot 开发要点总结(3)1. 日志有什么作用2. 日志格式2.1 日志框架原理 3. 日志的打印3.1 System.out.println3.2 使用日志框架3.3 日志级别3.3.1 设置默认日志显…

深度学习(36)—— 图神经网络GNN(1)

深度学习(36)—— 图神经网络GNN(1) 这个系列的所有代码我都会放在git上,欢迎造访 文章目录 深度学习(36)—— 图神经网络GNN(1)1. 基础知识2.使用场景3. 图卷积神经网…

提高测试用例质量的6大注意事项

在软件测试中,经常会遇到测试用例设计不完整,用例没有完全覆盖需求等问题,这样往往容易造成测试工作效率低下,不能及时发现项目问题,无形中增加了项目风险。 因此提高测试用例质量,就显得尤为重要。一般来说…

电脑开不了机如何解锁BitLocker硬盘锁

事情从这里说起,不想看直接跳过 早上闲着无聊,闲着没事干,将win11的用户名称改成了含有中文字符的用户名,然后恐怖的事情发生了,蓝屏了… 然后就是蓝屏收集错误信息,重启,蓝屏收集错误信息&…

C#小轮子:自动连续Ping网络地址

文章目录 前言Ping代码异步问题 前言 工作中,我们经常用到Ping这个指令,有时候我们需要Ping整个网段来查看这个网段上面有什么设备,哪些Ip地址是通的,这个时候就需要Ping指令 Ping 代码 我这个是批量Ping的代码,而…

python爬虫实战(2)--爬取某博热搜数据

1. 准备工作 使用python语言可以快速实现,调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义url url https://s.微博.com/top/summary?caterealtimehot定义请求头,微博请求数据需要cookie…

OpenAI允许网站阻止其网络爬虫;谷歌推出类似Grammarly的语法检查功能

🦉 AI新闻 🚀 OpenAI推出新功能,允许网站阻止其网络爬虫抓取数据训练GPT模型 摘要:OpenAI最近推出了一个新功能,允许网站阻止其网络爬虫从其网站上抓取数据训练GPT模型。该功能通过在网站的Robots.txt文件中禁止GPTB…

datax抽取库名带点的表遇到的问题

一、描述任务 使用Datax抽取mysql中的数据到hive的wedw_ods层中,mysql的库名为:b.p.n.p 表名为:bene_group 二、datax.json脚本生成 因为datax的脚本是自动生成的,生成的格式如下: {"core": {},"jo…

python接口自动化测试框架2.0,让你像Postman一样编写测试用例,支持多环境切换、多业务依赖、数据库断言等

项目介绍 接口自动化测试项目2.0 软件架构 本框架主要是基于 Python unittest ddt HTMLTestRunner log excel mysql 企业微信通知 Jenkins 实现的接口自动化框架。 前言 公司突然要求你做自动化,但是没有代码基础不知道怎么做?或者有自动化…

部署模型并与 TVM 集成

本篇文章译自英文文档 Deploy Models and Integrate TVM tvm 0.14.dev0 documentation 更多 TVM 中文文档可访问 →Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。 | Apache TVM 中文站 本节介绍如何将 TVM 部署到各种平台&…

搭建Repo服务器

1 安装repo 参考&#xff1a;清华大学开源软件镜像站:Git Repo 镜像使用帮助 2 创建manifest仓库 2.1 创建仓库 git init --bare manifest.git2.2 创建default.xml文件 default.xml文件内容&#xff1a; <?xml version"1.0" encoding"UTF-8" ?…

基于Googlenet深度学习网络的人员行为动作识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 1. 原理 1.1 深度学习与卷积神经网络&#xff08;CNN&#xff09; 1.2 GoogLeNet 2. 实现过程 2.1 数据预处理 2.2 构建网络模型 2.3 数据输入与训练 2.4 模型评估与调优 3. 应用领域…

STM32 LL库开发

一、STM32开发方式 标准库开发&#xff1a;Standard Peripheral Libraries&#xff0c;STDHAL库开发&#xff1a;Hardware Abstraction Layer&#xff0c;硬件抽象层LL库开发&#xff1a;Low-layer&#xff0c;底层库 二、HAL库与LL库开发对比 ST在推行HAL库的时候&#xff0c;…

C# Linq源码分析之Take方法

概要 Take方法作为IEnumerable的扩展方法&#xff0c;具体对应两个重载方法。本文主要分析第一个接收整数参数的重载方法。 源码解析 Take方法的基本定义 public static System.Collections.Generic.IEnumerable Take (this System.Collections.Generic.IEnumerable source…

【uniapp】uniapp使用微信开发者工具制作骨架屏:

文章目录 一、效果&#xff1a;二、过程&#xff1a; 一、效果&#xff1a; 二、过程&#xff1a; 【1】微信开发者工具打开项目&#xff0c;生成骨架屏&#xff0c;将wxml改造为vue页面组件&#xff0c;并放入样式 【2】页面使用骨架屏组件 【3】改造骨架屏&#xff08;去除…

以mod_jk方式整合apache与tomcat(动静分离)

前言&#xff1a; 为什么要整合apache和tomcat apache对静态页面的处理能力强&#xff0c;而tomcat对静态页面的处理不如apache&#xff0c;整合后有以下好处 提升对静态文件的处理性能 利用 Web 服务器来做负载均衡以及容错 更完善地去升级应用程序 jk整合方式介绍&#…

项目知识点记录

1.使用druid连接池 使用properties配置文件&#xff1a; driverClassName com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/book?useSSLtrue&setUnicodetrue&charsetEncodingUTF-8&serverTimezoneGMT%2B8 username root password 123456 #初始化链接数…

【验证码逆向专栏】最新某度旋转验证码 v2 逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

2021年09月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数字判断 输入一个字符,如何输入的字符是数字,输出yes,否则输出no 输入 一个字符 输出 如何输入的字符是数字,输出yes,否则输出no 样例1输入 样例1输入 5 样例1输出 yes 样例2输入 A 样例2输出 no 下面是一个使用C语言编写的数字判断程序的示例代码,根据输入的字符…