45.3.22附加自动配置和切片
每个切片提供一个或多个 @AutoConfigure… 注释,即定义应作为切片的一部分包括的自动配置。可以通过创建自定义 @AutoConfigure… 注释
或仅通过向测试添加 @ImportAutoConfiguration 来添加其他自动配置,如以下示例所示:
@RunWith(SpringRunner.class)
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
public class ExampleJdbcTests {
}
确保不使用常规 @Import 注释来导入自动配置,因为Spring Boot以特定方式处理它们。
45.3.23用户配置和切片
如果以合理的方式构造代码,默认情况下会使用 @SpringBootApplication 类 作为测试的配置。
然后,重要的是不要使用特定于其功能的特定区域的配置设置来丢弃应用程序的主类。
假设您正在使用Spring Batch,并依赖于它的自动配置。您可以按如下方式定义 @SpringBootApplication :
@SpringBootApplication
@EnableBatchProcessing
public class SampleApplication { ... }
因为此类是测试的源配置,所以任何切片测试实际上都会尝试启动Spring Batch,这绝对不是您想要做的。建议的方法是将特定于区域的配置移
动到与应用程序相同级别的单独 @Configuration 类,如以下示例所示:
@Configuration
@EnableBatchProcessing
public class BatchConfiguration { ... }
根据应用程序的复杂程度,您可能只有一个 @Configuration 类用于自定义,或者每个域区域有一个类。后一种方法允许您在必要
时使用 @Import 注释在其中一个测试中启用它。
混淆的另一个原因是类路径扫描。假设您以合理的方式构建代码,则需要扫描其他包。您的应用程序可能类似于以下代码:
@SpringBootApplication
@ComponentScan({ "com.example.app", "org.acme.another" })
public class SampleApplication { ... }
这样做会有效地覆盖默认的组件扫描指令,无论您选择哪个切片,都会扫描这两个包。例如, @DataJpaTest 似乎突然扫描应用程序的组件和用
户配置。同样,将自定义指令移动到单独的类是解决此问题的好方法。
如果这不是您的选项,您可以在测试的层次结构中的某处创建一个 @SpringBootConfiguration ,以便使用它。或者,您可以为
测试指定源,这会禁用查找默认源的行为。
45.3.24使用Spock测试Spring Boot应用程序
如果您希望使用Spock来测试Spring Boot应用程序,您应该将Spock的 spock-spring 模块的依赖项添加到您的应用程序的构建
中。spock-spring 将Spring的测试框架集成到Spock中。建议您使用Spock 1.2或更高版本从Spock的Spring框架和Spring Boot集成的许多改
进中受益。有关更多详细信息,请参阅Spock的Spring模块的文档。
45.4测试实用程序
90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。⇐ 立即查看
测试应用程序时通常有用的一些测试实用程序类打包为 spring-boot 的一部分。
45.4.1 ConfigFileApplicationContextInitializer
ConfigFileApplicationContextInitializer 是 ApplicationContextInitializer ,您可以将其应用于测试以加载Spring Boot
application.properties 个文件。当您不需要 @SpringBootTest 提供的全部功能时,可以使用它,如以下示例所示:
@ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
仅使用 ConfigFileApplicationContextInitializer 不能为 @Value("${… }") 注射提供支持。它唯一的工作是确保
将 application.properties 个文件加载到Spring的 Environment 中。对于 @Value 支持,您需要另外配
置 PropertySourcesPlaceholderConfigurer 或使用 @SpringBootTest ,它会为您自动配置一个。
45.4.2 TestPropertyValues
TestPropertyValues 可让您快速向 ConfigurableEnvironment 或 ConfigurableApplicationContext 添加属性。您可以使用 key=value
字符串调用它,如下所示:
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(env);
45.4.3 OutputCapture
OutputCapture 是一个JUnit Rule ,可用于捕获 System.out 和 System.err 输出。您可以将捕获声明为 @Rule ,然后使用 toString() 进行
断言,如下所示:
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class MyTest {
@Rule
public OutputCapture capture = new OutputCapture();
@Test
public void testName() throws Exception {
System.out.println("Hello World!");
assertThat(capture.toString(), containsString("World"));
}
45.4.4 TestRestTemplate
Spring Framework 5.0提供了一个新的 WebTestClient ,适用于 WebFlux集成测试以及 WebFlux和MVC端到端测试。
与 TestRestTemplate 不同,它为断言提供了流畅的API。
TestRestTemplate 是Spring RestTemplate 的便利替代品,可用于集成测试。您可以获得一个vanilla模板或一个发送基本HTTP身份验证(使
用用户名和密码)的模板。在任何一种情况下,模板都以一种测试友好的方式运行,不会在服务器端错误上抛出异常。建议(但不是强制性的)
使用Apache HTTP Client(版本4.3.2或更高版本)。如果您在类路径上有这个,那么 TestRestTemplate 通过适当地配置客户端来响应。如果
您确实使用Apache的HTTP客户端,则启用一些其他测试友好功能:
不遵循重定向(因此您可以断言响应位置)。
Cookie被忽略(因此模板是无状态的)。
TestRestTemplate 可以直接在集成测试中实例化,如以下示例所示:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
@Test
public void testRequest() throws Exception {
HttpHeaders headers = this.template.getForEntity(
"http://myhost.example.com/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
}
或者,如果您将 @SpringBootTest 注释与 WebEnvironment.RANDOM_PORT 或 WebEnvironment.DEFINED_PORT 一起使用,则可以注入完全配
置的 TestRestTemplate 并开始使用它。如有必要,可以通过 RestTemplateBuilder bean应用其他自定义设置。任何未指定主机和端口的
URL都会自动连接到嵌入式服务器,如以下示例所示:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleWebClientTests {
@Autowired
private TestRestTemplate template;
@Test
public void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class)
.getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration
static class Config {
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}