Spring Boot中单元测试数据库的切换策略

2023-11-07

问题缘起

单元测试默认情况下使用嵌入式数据库,例如H2。如果要切换为MySQL,直接移除H2驱动,在application.properties(yml)配置相应的连接信息,都不起作用。那该如何切换配置呢?

单元测试数据库

在SpringBoot的单元测试中,默认使用嵌入数据库,例如H2,HSQLDB等.默认情况下无需指定具体的嵌入数据库类型,只需要在pom.xml文件中加入相应的数据库驱动即可,示例如下:

<dependencies>
    <!-- 略去其余依赖 --->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
		</dependency>
</dependencies>

这里Scope指定其作用的范围,test表示其可以在测试中生效,即可单元测试程序会使用H2数据库。

使用MySQL作为测试数据库

如果不想使用H2之类的嵌入数据库,而是希望在实际运行和单元测试的数据库保持一致,该如何处理呢?

首先了解一下AutoConfigureTestDataBase.java的代码定义:

**
 * Annotation that can be applied to a test class to configure
 * a test database to use
 * instead of any application defined or auto-configured {@link DataSource}.
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.database")
public @interface AutoConfigureTestDatabase {
	@PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
	Replace replace() default Replace.ANY;
	EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;
	enum Replace {
		ANY,
		AUTO_CONFIGURED,
		NONE
	}
}

从类上的说明信息来看,其主要用来配置测试程序连接的数据库,这个注解的设置将覆盖自动配置的数据库信息。这里需要注意一下replace属性默认为ANY,没有做数据库约束。

connection:默认读取classpath包中扫描到的数据库驱动包信息,其默认使用嵌入式数据库。

定义抽象BaseJPA的基类:

@RunWith(SpringRunner.class)
@DataJpaTest
@EnableJpaRepositories(basePackages = "org.demo.data.dao")
@EntityScan(basePackages="org.demo.data.dao")
@TestPropertySource(value= {"classpath:application-unittest.properties"})
@ContextConfiguration(classes= {AuditorConfig.class}) 
@Getter
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public abstract class BaseJPA {
    @Autowired
    private TestEntityManager testEntityManager;
}

这里定义了基于SpringDataJPA的测试基类,封装了常用的配置信息:

  • RunWith:指定测试程序运行的环境
  • DataJpaTest 用于指定当前测试程序用于测试DAO层,其中默认启用数据操作的相关内容,详细功能可以参照其类定义
  • EnableJpaRepositories 指定Repository的目录
  • EntityScan 指定SpringDataJPA实体类的目录位置
  • TestPropertySource 指定系统自定义的配置文件,用以替代默认的application.properties(yml)
  • ContextConfiguration 指定当前应用使用的配置类,设置相应的配置信息
  • Getter 为Lombok包的注解,用以生成Getter方法
  • AutoConfigureTestDatabase 用于指定测试程序依赖的数据库,这里使用None,不再从pom.xml文件中加载嵌入式数据库驱动。而是直接从系统配置文件application.properties中读取相应的数据库连接信息。

修改POM文件中的依赖

在使用MySQL作为单元测试的数据库,需要调整pom文件中MySQL的驱动程序包依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency> 

这里需要将其scope表示其只在系统运行之时,会被使用到。移除scope设置之后,将使用默认值,compile。其将作用与表示为当前依赖参与项目的编译、测试和运行阶段,属于强依赖。例如:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency> 

如果不能正确设置scope,在启动过程中,会报出如下错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
	... 52 more
Caused by: java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.
	at org.springframework.util.Assert.state(Assert.java:73)
	at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactory.getEmbeddedDatabase(TestDatabaseAutoConfiguration.java:185)
	at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean.afterPropertiesSet(TestDatabaseAutoConfiguration.java:151)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1830)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1767)
	... 63 more

从上述错误信息可知,默认情况下,会从classpath路径中搜索使用嵌入式数据库的驱动,这里报错的原因是由于没有在classpath中找到有效的数据库驱动。

测试代码

UserRepository是基于JpaRepository扩展而来的接口,其中基于方法名定义了若干查询,针对其中方法的单元测试:

@Slf4j
public class AnnotatedQueryUserTest extends BaseJPA {
    @Autowired
    private UserRepository userRepository;

    @Test
    @Sql(value="classpath:sqls/dao/user_annotated_query.sql")
    public void testCustomResult() {
        List<UserCount> userCountList = this.userRepository.listGroupedUser(0L);
        log.info("userCount List:{}", userCountList.size());
        Assert.assertThat(userCountList, hasSize(3));

        List<UserEntity> userEntities = this.userRepository.findCustomByNameAndSex("Robin Li", Gender.MALE);
        log.info("User Entity count:{}", userEntities.size());
        Assert.assertThat(userEntities, hasSize(1));
    }
}

总结

在本文中重点需要讲述的是如何自定义单元测试中的数据库类型,默认情况是使用嵌入式数据库。例如从嵌入式数据库H2切换到MySQL数据库,其中的关键点是AutoConfigureTestDatabase以及其中的replace属性。本文中使用了SpringDataJPA作为数据库中间层。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot中单元测试数据库的切换策略 的相关文章

随机推荐

  • [动态规划] leetcode 416. 分割等和子集

    问题描述 分割等和子集 给你一个只包含正整数的非空数组 nums 请你判断是否可以将这个数组分割成两个子集 使得两个子集的元素和相等 例子 输入nums 1 5 11 5 输出true 动态规划求解 这是一个0 1背包问题的变种 也就是每种
  • Idea工具使用经典总结

    安装教程 下载地址 https www jetbrains com idea download section windows 准备idea ideaIU 2017 2 3 exe 软件与激活包 JetbrainsCrack 2 6 9 r
  • 设备节点如何与设备驱动关联

    1 上层应用如何调用设备驱动 1 在linux中一切皆是文件 设备驱动程序对上层应用程序来说和普通文件没什么差异 2 上层应用程序通过设备节点来访问驱动程序 在驱动程序注册到内核后 用申请到的主次设备号来创建设备节点 2 向内核注册字符驱动
  • 【已解决】Error: Unable to access jarfile .\xxxx.jar

    报错类型 Error Unable to access jarfile xxxx jar 复现工具的时候 通过命令 java jar xxxx jar 运行 jar 包报了这个错误 报错原因是 在命令行中出现的路径下找不到 xxxx jar
  • micro-app在vue-element-admin中一些使用研究

    1 简述 本文承接上一篇micro app在vue element admi中的搭建 对micro app在vue element admin中的一些平时开发中常用的功能做了一些研究 本文代码 2 路由 关于路由 这边从两方面进行研究 一方
  • ass字幕格式

    ssa ass字幕格式全解析 内容 一 概述 二 文件各个部分解析 三 各种类型的行 四 Script Info 部分的标题行 五 v4 Styles 部分的风格行Style 六 Events 事件部分的对话行Dialogue 七 Even
  • 高通功耗调试18之Tsensor中断频繁触发导致低温下待机功耗高的问题

    问题背景 在内核4 9及之后的版本 低于5C环境温度下待机 由于触发了Tsensor的低温保护机制 可能会遇到较频繁 的tsens中断 11 22 07 12 27 914969 0 0 W GICv3 gic show resume ir
  • [echarts]柱状图的点击事件

    先来一段简洁的写echarts图表的代码 这样获取echarts的dom节点是因为 如果将柱状图封装成了一个组件 在一个页面中多次使用 若还是按常规获取dom节点 会报一个警告 let charts echarts getInstanceB
  • Linux驱动开发(应用程序如何调用驱动)

    1 添加读写接口 1 在应用代码中 2 在驱动代码中 2 应用和驱动之间的数据交换 1 copy from user 用来将数据从用户空间复制到内核空间 2 copy to user 用来将数据从内核空间复制到用户空间 3 write和re
  • DAPM之一:概述

    DAPM Dynamic Audio Power Management 对应结构体是snd soc dapm widget和snd soc dapm route 对应的操作函数是snd soc dapm new controls snd s
  • C++对象模型和this指针

    C 对象模型和this指针 成员变量和成员函数分开存储 在C 中 类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上 include
  • 逐梦C++补遗篇之一:cout与cerr的区分

    逐梦C 补遗篇之一 cout与cerr的区分 1 从定义看区别 cout 标准输出流 带缓冲 默认输出目的地为屏幕 可以被重定向 cerr 标准错误输出 不带缓冲 输出目的地为屏幕 一般不被重定向 缓冲 带缓冲 就是系统会为你分配一个缓冲区
  • 精彩观点一览

    7月20日下午 大模型的发展路径论坛于北京成功举办 大模型的发展路径论坛作为2023中国互联网大会的分论坛之一 由中国互联网协会人工智能工作委员会承办 中国信通院云计算与大数据研究所 华为云大数据与AI业务协办 并得到阿里云 北京智源研究院
  • 理解什么是 JMM

    理解什么是 JMM 本文已收录至 GitHub https github com yifanzheng java notes Java 虚拟机是一个完整的计算机的一个模型 因此这个模型自然也包含一个内存模型 Java 内存模型 也就是说 J
  • 最短路算法——Dijkstra

    Dijkstra 在大多数最短路径问题中 Dijkstra 算法是最常用 效率最高的 它是一种 单源 最短路径算法 一次计算能得到从一个起点 s 到其他所有点的最短距离长度 最短路径的途径点 一 Dijkstra的算法思想 Dijkstra
  • Upload LABS Pass-6

    第六关在后端使用了黑名单 并过滤了大小写和点 但未过滤空格 我们使用代理抓包在后缀名中添加空格 即可绕过黑名单 准备一个 6 php 文件 内容为一句话木马 上传 6 php 文件 并开启代理 此处使用 Burp Suite 拦截请求 在文
  • python旋转矩阵_python将四元数变换为旋转矩阵

    import numpy as np from autolab core import RigidTransform 写上用四元数表示的orientation和xyz表示的position orientation y 0 697127881
  • java代码的四层结构

    一 util包 放共同类的包 整个项目中 可以共用的一些代码 例如 一些常用的字符串的非空验证 身份证或者电话号码的正则验证等等 1 JDBC类功能的封装 package util import java io IOException im
  • VsCode中修改/重置gitlab远程仓库地址

    A 更换git远程仓库地址 1 查看当前remotes git remote v 2 修改remotes git remote set url origin https github com test test git B 重置git远程仓
  • Spring Boot中单元测试数据库的切换策略

    问题缘起 单元测试默认情况下使用嵌入式数据库 例如H2 如果要切换为MySQL 直接移除H2驱动 在application properties yml 配置相应的连接信息 都不起作用 那该如何切换配置呢 单元测试数据库 在SpringBo