每次执行@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.rhkb.cn/news/81283.html

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

相关文章

两个多选框(select)之间值的左右上下移动

<!DOCTYPE html> <html> <head><meta charset"utf-8"><title>两个多选框(select)之间值的左右上下移动</title> </head> <script src"https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>&…

看重ARM?苹果、三星、英伟达等知名企业纷纷表示加大投资

根据日经亚洲的报道&#xff0c;芯片设计公司Arm计划进行首次公开募股并在纳斯达克上市。苹果、三星电子、英伟达、英特尔等知名企业计划在Arm美股上市后投资该公司。 据悉&#xff0c;Arm将于9月份上市&#xff0c;预计估值将达到至少600亿美元&#xff08;约合4314亿元人民币…

Spring Boot多级缓存实现方案

1.背景 缓存&#xff0c;就是让数据更接近使用者&#xff0c;让访问速度加快&#xff0c;从而提升系统性能。工作机制大概是先从缓存中加载数据&#xff0c;如果没有&#xff0c;再从慢速设备(eg:数据库)中加载数据并同步到缓存中。 所谓多级缓存&#xff0c;是指在整个系统架…

Jmeter录制HTTPS脚本

Jmeter录制HTTPS脚本 文章目录 添加“HTTP代理服务器”设置浏览器代理证书导入存在问题 添加“HTTP代理服务器” 设置浏览器代理 保持端口一致 证书导入 点击一下启动让jmeter自动生成证书&#xff0c;放在bin目录下&#xff1a; 打开jmeter的SSL管理器选择刚刚生成的证书&…

Linux root用户执行修改密码命令,提示 Permission denied

问题 linux系统中&#xff08;ubuntu20&#xff09;&#xff0c;root用户下执行passwd命令&#xff0c;提示 passwd: Permission denied &#xff0c;如下图&#xff1a; 排查 1.执行 ll /usr/bin/passwd &#xff0c;查看文件权限是否正确&#xff0c;正常情况是 -rwsr-xr…

20230807通过ffmpeg将DTS编码的AUDIO音频转换为AAC编码

20230807通过ffmpeg将DTS编码的AUDIO音频转换为AAC编码 2023/8/7 20:04 ffmpeg dts 转AAC 缘起&#xff1a;由于网上找的电影没有中文字幕&#xff0c;有内置的英文字幕&#xff0c;但是还是通过剪映/RP2023识别一份英文字幕备用&#xff01; I:\Downloads\2005[红眼航班]Red E…

一、MySql前置知识

文章目录 一、什么是数据库&#xff08;一&#xff09;存储数据用文件就可以了&#xff0c;为什么还要弄个数据库?&#xff08;二&#xff09;数据库存储介质&#xff1a;&#xff08;三&#xff09;主流数据库 二、数据库基本操作&#xff08;一&#xff09;连接服务器&#…

基于Spring Boot的医院预约挂号网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的医院预约挂号网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spring…

Linux 远程登录

Linux 远程登录 Linux 一般作为服务器使用&#xff0c;而服务器一般放在机房&#xff0c;你不可能在机房操作你的 Linux 服务器。 这时我们就需要远程登录到Linux服务器来管理维护系统。 Linux 系统中是通过 ssh 服务实现的远程登录功能&#xff0c;默认 ssh 服务端口号为 2…

机器学习深度学习——从全连接层到卷积

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——非NVIDIA显卡怎么做深度学习&#xff08;坑点排查&#xff09; &#x1f4da;订阅专栏&#xff1a;机器…

流量、日志分析

流量分析 知识点&#xff1a; 流量包分析简介 - CTF Wiki (ctf-wiki.org) Wireshark 基本语法&#xff0c;基本使用方法&#xff0c;及包过虑规则_wireshark语法_竹痕的博客-CSDN博客 MISC&#xff1a;流量包取证&#xff08;pcap文件修复、协议分析、数据提取&#xff09;…

【linux-keepalive】keepalive避免单点故障,高可用配置

keepalive: [rootproxy ~]# yum install -y keepalived [rootproxy ~]# vim /etc/keepalived/keepalived.conf global_defs {router_id proxy1 //设置路由ID号vrrp_iptables //不添加任何防火墙规则 } vrrp_instance V…

AI Chat 设计模式:13. 代理模式

本文是该系列的第十三篇&#xff0c;采用问答式的方式展开&#xff0c;和前面的文章有一些不同&#xff0c;我不再进行提问了&#xff0c;改为由 GPT 1 号提问&#xff0c;GPT 2 号作答&#xff0c;每一节的小标题是我从 GPT 1 号的提问中总结出来的。我现在是完完全全的旁观者…

H. HEX-A-GONE Trails 2023“钉耙编程”中国大学生算法设计超级联赛(7)hdu7354

Problem - 7354 题目大意&#xff1a;有一棵n个点的树&#xff0c;A和B分别从点x&#xff0c;y开始&#xff0c;每轮可以移动到一个相邻节点&#xff0c;但如果某个节点有人访问过&#xff0c;则两人都不能访问那个节点&#xff0c;先没有点可走的人输&#xff0c;问A有没有必…

LeetCode:Hot100的python版本

94. 二叉树的中序遍历

DOM基础获取元素+事件基础+操作元素

一.DOM简介 DOM&#xff0c;全称“Document Object Model&#xff08;文档对象模型&#xff09;”&#xff0c;它是由W3C定义的一个标准。 在实际开发中&#xff0c;我们有时候需要实现鼠标移到某个元素上面时就改变颜色&#xff0c;或者动态添加元素或者删除元素等。其实这些效…

SpringBoot复习:(22)ConfigurationProperties和@PropertySource配合使用及JSR303校验

一、配置类 package cn.edu.tju.config;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component;Component ConfigurationPropertie…

CentOS软件包管理rpm、yum

一、软件包概述 Linux常见软件包分为两种&#xff0c;分别是源代码包、二进制文件包。源代码包是没有经过编译的包&#xff0c;需要经过GCC、C编译器编译才能运行&#xff0c;文件内容包含源代码文件&#xff0c;通常以.tar.gz、.zip、.rar结尾&#xff1b;二进制包无需编译&am…

数据结构 二叉树(一篇基本掌握)

绪论 雄关漫道真如铁&#xff0c;而今迈步从头越。 本章将开始学习二叉树&#xff08;全文共一万两千字&#xff09;&#xff0c;二叉树相较于前面的数据结构来说难度会有许多的攀升&#xff0c;但只要跟着本篇博客深入的学习也可以基本的掌握基础二叉树。 话不多说安全带系好&…

31 对集合中的字符串,按照长度降序排列

思路&#xff1a;使用集合的sort方法&#xff0c;新建一个Comparator接口&#xff0c;泛型是<String>&#xff0c;重写里面的compare方法。 package jiang.com; import java.util.Arrays; import java.util.Comparator; import java.util.List;public class Practice4 {…