前言
作为一个Java开发,我相信大家或多或少都写过单元测试,特别是SpringBoot项目,它的test组件提供了大量的功能来帮助我们高效的完成单元测试。 这个系列我会分几个部分,通过实际的单元测试中遇到的问题场景,来分享一下如何利用SpringBootTest来快速实现或解决对应的单元测试需求及问题。
配置隔离
我们在进行单元测试时,特别是在本机运行时,经常会有这样的场景:单元测试的配置中的部分特定项需要和正式的配置区分(正式的配置是指发布到服务器上的配置,不管是开发、测试还是生产环境),比较常见的比如端口、文件目录信息等等和环境相关的配置。
这种情况我们一般怎么处理? 可能很多人都是把正式配置copy一份,然后把其中的特定项改为测试所需要的配置,用于单元测试,比如:
@ContextConfiguration("/test-config.properties")
class ApplicationContextTests {
// do something
}
这样在大部分场景下确实可以,但是还是存在问题,比如项目使用了配置中心怎么办,程序会使用配置中心的配置;还有如果正式配置变更了,我都要同步的来修改test-config.properties
,非常麻烦。
覆盖配置
@TestPropertySource
Spring帮我们考虑到了这些情况,我们可以直接通过覆盖PropertySource来解决:
/**
* 指定配置文件
*/
@ContextConfiguration
@TestPropertySource("/test.properties")
class PropertySourceTests {
// do something
}
/**
* 指定某个属性
*/
@ContextConfiguration
@TestPropertySource(properties = "port=8808")
class PropertySourceTests {
// do something
}
我们可以直接通过@TestPropertySource
注解来指定配置文件或者只指定某个配置项,通过@TestPropertySource
指定的配置在SpringBoot中的优先级比较高,会覆盖通过@PropertySource注入的属性。
动态配置
在进行配置隔离时,还有一种场景可能是我们在运行单元测试时,还不知道配置是什么,需要在运行时来决定的
,比如本地运行单元测试和CICD运行时,配置可能并不一样;又或者你的配置是从另一个服务获取的,而不是写死的
。那这时候我们该怎么处理?
@DynamicPropertySource
我们可以通过@DynamicPropertySource
配置来在运行时动态的注入配置:
@ContextConfiguration
class DynamicPropertySourceTests {
@DynamicPropertySource
static void dynamicProperties(DynamicPropertyRegistry registry) {
registry.add("server.port", () -> getFromRedis());
}
// do something
}
@DynamicPropertySource
注解可以标记在方法上,通过DynamicPropertyRegistry
类来将配置动态的进行注入,这样我们就可以在运行时来决定我们的配置项了。
@DynamicPropertySource的优先级是很高的,它的优先级比上面我们说到的@TestPropertySource以及操作系统环境变量、程序的properties、@PropertySource注入的属性都要高。
总结
上面总结了几种通过不同方式来隔离正式配置与测试配置的方法,实际在一般的业务场景下,如果不是非常注重单元测试的质量,我们可能不一定应用得上。但是在合适的场景下,通过SpringBoot提供的对应的功能,我们可以更简单且优雅的实现对应的功能。