1. Overview
1.概述
It’s well known that auto-configuration is one of the key features in Spring Boot, but testing auto-configuration scenarios can be tricky.
众所周知,自动配置是Spring Boot的关键功能之一,但测试自动配置场景可能很棘手。
In the following sections, we’ll show how ApplicationContextRunner simplifies auto-configuration testing.
在以下章节中,我们将展示ApplicationContextRunner如何简化自动配置测试。
2. Test Auto-Configuration Scenarios
2.测试自动配置情景
ApplicationContextRunner is a utility class which runs the ApplicationContext and provides AssertJ style assertions. It’s best used as a field in test class for shared configuration and we make customizations in each test afterward:
ApplicationContextRunner是一个实用类,它运行ApplicationContext并提供AssertJ风格的断言。它最好作为测试类的一个字段用于共享配置,之后我们在每个测试中进行自定义。
private final ApplicationContextRunner contextRunner
= new ApplicationContextRunner();
Let’s move on to show its magic by testing a few cases.
让我们继续通过测试几个案例来展示它的魔力。
2.1. Test Class Condition
2.1.测试等级条件
In this section, we’re going to test some auto-configuration classes which use @ConditionalOnClass and @ConditionalOnMissingClass annotations:
在本节中,我们将测试一些使用@ConditionalOnClass和@ConditionalOnMissingClass注释的自动配置类。
@Configuration
@ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
protected static class ConditionalOnClassConfiguration {
@Bean
public String created() {
return "This is created when ConditionalOnClassIntegrationTest "
+ "is present on the classpath";
}
}
@Configuration
@ConditionalOnMissingClass(
"com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest"
)
protected static class ConditionalOnMissingClassConfiguration {
@Bean
public String missed() {
return "This is missed when ConditionalOnClassIntegrationTest "
+ "is present on the classpath";
}
}
We’d like to test whether the auto-configuration properly instantiates or skips the created and missed beans given expected conditions.
我们想测试一下,在预期条件下,自动配置是否正确地实例化或跳过了创建的和遗漏的Bean。
ApplicationContextRunner gives us the withUserConfiguration method where we can provide an auto-configuration on demand to customize the ApplicationContext for each test.
ApplicationContextRunner为我们提供了withUserConfiguration方法,我们可以根据需要提供一个自动配置,为每个测试定制ApplicationContext。
The run method takes a ContextConsumer as a parameter which applies the assertions to the context. The ApplicationContext will be closed automatically when the test exits:
run方法需要一个ContextConsumer作为参数,将断言应用于上下文。 测试退出时,ApplicationContext将被自动关闭。
@Test
public void whenDependentClassIsPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.run(context -> {
assertThat(context).hasBean("created");
assertThat(context.getBean("created"))
.isEqualTo("This is created when ConditionalOnClassIntegrationTest "
+ "is present on the classpath");
});
}
@Test
public void whenDependentClassIsPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.run(context -> {
assertThat(context).doesNotHaveBean("missed");
});
}
Through the preceding example, we see the simpleness of testing the scenarios in which a certain class is present on the classpath. But how are we going to test the converse, when the class is absent on the classpath?
通过前面的例子,我们看到了测试某个类在classpath上存在的情景的简单性。但是,当类在classpath上不存在时,我们将如何测试反过来的情况?
This is where FilteredClassLoader kicks in. It’s used to filter specified classes on the classpath at runtime:
这就是FilteredClassLoader的作用。它被用来在运行时过滤classpath上的指定类。
@Test
public void whenDependentClassIsNotPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run((context) -> {
assertThat(context).doesNotHaveBean("created");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}
@Test
public void whenDependentClassIsNotPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run((context) -> {
assertThat(context).hasBean("missed");
assertThat(context).getBean("missed")
.isEqualTo("This is missed when ConditionalOnClassIntegrationTest "
+ "is present on the classpath");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}
2.2. Test Bean Condition
2.2.测试Bean条件
We’ve just looked at testing @ConditionalOnClass and @ConditionalOnMissingClass annotations, now let’s see what things look like when we are using @ConditionalOnBean and @ConditionalOnMissingBean annotations.
我们刚刚看了测试@ConditionalOnClass和@ConditionalOnMissingClass注解,现在让我们看看当我们使用@ConditionalOnBean和@ConditionalOnMissingBean注解时的情况。
To make a start, we similarly need a few auto-configuration classes:
为了开个头,我们同样需要几个自动配置类。
@Configuration
protected static class BasicConfiguration {
@Bean
public String created() {
return "This is always created";
}
}
@Configuration
@ConditionalOnBean(name = "created")
protected static class ConditionalOnBeanConfiguration {
@Bean
public String createOnBean() {
return "This is created when bean (name=created) is present";
}
}
@Configuration
@ConditionalOnMissingBean(name = "created")
protected static class ConditionalOnMissingBeanConfiguration {
@Bean
public String createOnMissingBean() {
return "This is created when bean (name=created) is missing";
}
}
Then, we’d call the withUserConfiguration method like the preceding section and send in our custom configuration class to test if the auto-configuration appropriately instantiates or skips createOnBean or createOnMissingBean beans in different conditions:
然后,我们会像上一节一样调用withUserConfiguration方法,并送入我们的自定义配置类,以测试自动配置是否在不同条件下适当地实例化或跳过createOnBean或createOnMissingBeanBean。
@Test
public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
this.contextRunner.withUserConfiguration(
BasicConfiguration.class,
ConditionalOnBeanConfiguration.class
)
// ommitted for brevity
}
@Test
public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
// ommitted for brevity
}
2.3. Test Property Condition
2.3.测试属性条件
In this section, let’s test the auto-configuration classes which use @ConditionalOnProperty annotations.
在本节中,让我们测试使用@ConditionalOnProperty注释的自动配置类。
First, we need a property for this test:
首先,我们需要为这个测试提供一个属性。
com.baeldung.service=custom
After that, we write nested auto-configuration classes to create beans based on the preceding property:
之后,我们编写嵌套的自动配置类,根据前面的属性创建Bean。
@Configuration
@TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
protected static class SimpleServiceConfiguration {
@Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
@ConditionalOnMissingBean
public DefaultService defaultService() {
return new DefaultService();
}
@Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
@ConditionalOnMissingBean
public CustomService customService() {
return new CustomService();
}
}
Now, we’re calling the withPropertyValues method to override the property value in each test:
现在,我们要调用withPropertyValues方法来覆盖每个测试中的属性值。
@Test
public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=custom")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("customService");
SimpleService simpleService = context.getBean(CustomService.class);
assertThat(simpleService.serve()).isEqualTo("Custom Service");
assertThat(context).doesNotHaveBean("defaultService");
});
}
@Test
public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=default")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("defaultService");
SimpleService simpleService = context.getBean(DefaultService.class);
assertThat(simpleService.serve()).isEqualTo("Default Service");
assertThat(context).doesNotHaveBean("customService");
});
}
3. Conclusion
3.总结
To sum up, this tutorial just showed how to use ApplicationContextRunner to run the ApplicationContext with customizations and apply assertions.
总而言之,本教程只是展示了如何使用ApplicationContextRunner来运行具有自定义功能的ApplicationContext并应用断言。
We covered the most frequently used scenarios in here instead of an exhaustive list of how to customize the ApplicationContext.
我们在这里涵盖了最常用的场景,而不是详尽地列出了如何定制ApplicationContext.。
In the meantime, please bear in mind that the ApplicationConetxtRunner is for non-web applications, so consider WebApplicationContextRunner for servlet-based web applications and ReactiveWebApplicationContextRunner for reactive web applications.
同时,请记住,ApplicationConetxtRunner是针对非网络应用的,所以考虑WebApplicationContextRunner用于基于Servlet的网络应用,ReactiveWebApplicationContextRunner用于反应性网络应用。
The source code for this tutorial can be found over on GitHub.
本教程的源代码可以在GitHub上找到over。