JUnit 5 根据激活的 Profile 进行测试
1、概览
我们经常需要为开发和部署过程中的不同阶段创建不同的配置。在 Spring Boot 应用中,我们可以为每个不同的阶段定义一个 Spring Profile 并为其创建专门的测试。
在本教程中,我们将介绍如何使用 JUnit 5 基于激活的 Spring Profile 来进行测试。
2、项目设置
首先,在我们的项目中添加 spring-boot-starter-web 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
现在,让我们创建一个简单的 Spring Boot 应用:
@SpringBootApplication
public class ActiveProfileApplication {
public static void main (String [] args){
SpringApplication.run(Application.class);
}
}
最后,创建 application.yaml
配置文件。
3、Spring Profile
Spring Profile 提供了一种方法,通过对每个环境的特定配置进行分组,来定义和管理不同的环境。
通过激活特定的 Profile,我们可以在不同的配置之间轻松切换。
3.1、在 Properties 中激活 Profile
我们可以在 application.yaml
文件中指定要激活的 profile:
spring:
profiles:
active: dev
现在 Spring 将检索激活的 profile 的所有属性,并将所有专用 Bean 加载到 application context 中。
在本文中,我们将为每个 profile 创建专用的 properties 文件。
我们分别为 test
环境和 prod
环境创建两个 profile,每个 profile 都带有不同值的 profile.property.value
配置属性。
创建 application-prod.yaml
文件,并添加 profile.property.value
属性:
profile:
property:
value: This the the application-prod.yaml file
同样地,创建和配置 application-test.yaml
:
profile:
property:
value: This the the application-test.yaml file
最后,让我们在 application.yaml
中添加相同的属性:
profile:
property:
value: This the the application.yaml file
现在,让我们编写一个简单的测试,以验证应用程序在此环境中的行为是否符合预期:
@SpringBootTest(classes = ActiveProfileApplication.class)
public class DevActiveProfileUnitTest {
@Value("${profile.property.value}")
private String propertyString;
@Test
void whenDevIsActive_thenValueShouldBeKeptFromApplicationYaml() {
Assertions.assertEquals("This the the application.yaml file", propertyString);
}
}
变量 propertyString
注入的值来自 application.yaml
中定义的属性值。这是因为在测试执行过程中激活的 profile 是 dev
,而且没有为该 profile 定义 properties 文件。
3.2、在测试类上激活 Profile
通过设置 spring.profiles.active
属性,我们可以激活相应的 profile 并加载与其相关的配置文件。
但在某些情况下,我们可能希望使用特定的 profile 执行测试类,覆盖 properties 文件中定义的 active profile。
因此,我们可以使用与 JUnit 5 兼容的注解 @ActiveProfiles
,它声明了在加载测试类的 application context 时要使用的 profile。
也就是说,如果我们用 @ActiveProfiles
注解一个测试类,并将 value
属性设置为 test
,那么类中的所有测试都将基于该 test
profile:
@SpringBootTest(classes = ActiveProfileApplication.class)
@ActiveProfiles(value = "test")
public class TestActiveProfileUnitTest {
@Value("${profile.property.value}")
private String propertyString;
@Test
void whenTestIsActive_thenValueShouldBeKeptFromApplicationTestYaml() {
Assertions.assertEquals("This the the application-test.yaml file", propertyString);
}
}
value
属性是 profiles
属性的别名,是一个字符串数组(String[]
)。也就是说,可以指定多个激活的 profile。
例如,如果我们想指定激活的 profile 为 test
和 prod
,可以使用:
@ActiveProfiles({"prod", "test"})
这样,application context 就可以使用 test
和 prod
profile 的特定属性进行配置。
配置将按照列出的顺序应用。如果不同 profile 的配置发生冲突,则后面的会覆盖前面的。
根据不同的 profile 运行测试对于确保应用程序在不同环境中正确运行至关重要。不过,在其他环境中执行为特定 profile 设计的测试可能会带来很大风险。例如,在本地机器上运行测试时,我们可能会无意中运行为生产环境设计的测试。
为了避免这种情况,我们需要找到一种根据激活的 profile 过滤测试执行的方法。
4. @EnabledIf
注解
在 JUnit 4 中,可以使用 @IfProfileValue 注解有条件地执行测试。该注解指定了执行测试必须满足的条件。
但当我们的单元测试框架是 JUnit 5 时,我们应避免使用 @IfProfileValue
,因为当前版本已不再支持它。
我们可以使用 @EnabledIf
,这是一种根据条件启用或禁用方法或类的注解。
Junit 5 也提供了一个 @EnabledIf 注解。需要注意,我们应确保导入的是 Spring 提供的注解,以避免混淆。
该注解的属性如下:
value
:条件表达式,必须为true
才能启用测试类或单个测试。expression
: 条件表达式,value 的别名,注解了@AliasFor
。loadContext
: 指定是否需要加载 context 才能判断条件。默认值为false
reason
:解释为何需要这一条件。
为了计算、使用在 Spring Application Context 中定义的值的条件(如激活的 profile),我们应将 boolean 属性 loadContext
设为 true
。
4.1、在单个激活的 Profile 下运行测试
如果只想在单个 profile 处于激活状态时运行测试类,我们可以在 value
属性中使用 SPEL 函数进行判断:
#{environment.getActiveProfiles()[0] == 'prod'}
在该函数中,environment
变量是一个实现了 Enviroment
的对象。因此 environment.getActiveProfiles()
返回当前环境中激活的 profile 数组,而 [0]
则访问该数组的第一个元素。
在测试中添加注解:
@SpringBootTest(classes = ActiveProfileApplication.class)
@EnabledIf(value = "#{environment.getActiveProfiles()[0] == 'prod'}", loadContext = true)
public class ProdActiveProfileUnitTest {
@Value("${profile.property.value}")
private String propertyString;
@Test
void whenProdIsActive_thenValueShouldBeKeptFromApplicationProdYaml() {
Assertions.assertEquals("This the the application-prod.yaml file", propertyString);
}
}
然后,让我们通过 @ActiveProfiles
激活 prod
profile:
@SpringBootTest(classes = ActiveProfileApplication.class)
@EnabledIf(value = "#{environment.getActiveProfiles()[0] == 'prod'}", loadContext = true)
@ActiveProfiles(value = "prod")
public class ProdActiveProfileUnitTest {
@Value("${profile.property.value}")
private String propertyString;
@Test
void whenProdIsActive_thenValueShouldBeKeptFromApplicationProdYaml() {
Assertions.assertEquals("This the the application-prod.yaml file", propertyString);
}
}
因此,我们类中的测试将始终在 prod
profile 下运行,并且仅在当前唯一激活的 profile 为 prod
时才会运行。
4.2、在多个激活的 Profile 下运行测试
如果我们想在不同的 profile 下执行测试,可以在 value
或 expression
属性中使用 SPEL 函数进行判断。
#{{'test', 'prod'}.contains(environment.getActiveProfiles()[0])}
表达式解释:
{'test', 'prod'}
指定了一组在 Spring 应用程序中定义的两个 profile 名称。.contains(environment.getActiveProfiles()[0])
检查数组的第一个元素是否包含在前面定义的集合中。
让我们在测试类中添加 @EnableIf
注解:
@SpringBootTest(classes = ActiveProfileApplication.class)
@EnabledIf(value = "#{{'test', 'prod'}.contains(environment.getActiveProfiles()[0])}", loadContext = true)
@ActiveProfiles(value = "test")
public class MultipleActiveProfileUnitTest {
@Value("${profile.property.value}")
private String propertyString;
@Autowired
private Environment env;
@Test
void whenDevIsActive_thenValueShouldBeKeptFromDedicatedApplicationYaml() {
String currentProfile = env.getActiveProfiles()[0];
Assertions.assertEquals(String.format("This the the application-%s.yaml file", currentProfile), propertyString);
}
}
因此,当激活的 profile 为 test
或 prod
时,我们类中的测试将始终运行。
5、总结
在本文中,我们学习了如何使用 JUnit 5 注解根据激活的 Spring profile 执行测试、如何在测试类上启用 profile,以及如何在一个或多个特定 profile 处于激活状态时执行测试。
参考:https://www.baeldung.com/spring-boot-junit-5-testing-active-profile