likes
comments
collection
share

SpringBoot单元测试实践——配置隔离篇

作者站长头像
站长
· 阅读数 14

前言

作为一个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提供的对应的功能,我们可以更简单且优雅的实现对应的功能。