41. Testing
Spring Boot 提供了许多 Util 和 Comments,可以在测试应用程序时提供帮助。测试支持由两个模块提供; spring-boot-test
包含核心项目,而spring-boot-test-autoconfigure
支持自动配置测试。
大多数开发人员只会使用spring-boot-starter-test
'Starter',它会导入两个 Spring Boot 测试模块以及 JUnit,AssertJ,Hamcrest 和许多其他有用的库。
41.1 测试范围依赖性
如果您使用spring-boot-starter-test
'Starter'(位于test
scope
中),则会找到以下提供的库:
-
JUnit —用于 Java 应用程序的单元测试的事实上的标准。
-
Spring Test和 Spring Boot 测试—对 Spring Boot 应用程序的 Util 和集成测试支持。
-
AssertJ —流畅的 assert 库。
-
Hamcrest —匹配器对象库(也称为约束或谓词)。
-
Mockito — Java 模拟框架。
-
JSONassert — JSON 的 assert 库。
-
JsonPath — JSON 的 XPath。
Note
默认情况下,Spring Boot 使用 Mockito1.x。但是,如果您愿意,也可以使用 2.x。
这些是我们通常在编写测试时发现有用的通用库。如果不符合您的需求,您可以自由添加其他测试依存关系。
41.2 测试 Spring 应用程序
依赖注入的主要优点之一是,它应该使您的代码更易于进行单元测试。您只需使用new
运算符即可实例化对象,甚至无需使用 Spring。您也可以使用* mock objects *代替 true 的依赖。
通常,您需要超越“单元测试”并开始“集成测试”(实际上需要在过程中使用 Spring ApplicationContext
)。能够进行集成测试而无需部署应用程序或连接到其他基础结构,这很有用。
Spring 框架包括一个专用的测试模块,用于进行此类集成测试。您可以直接向org.springframework:spring-test
声明依赖项,也可以使用spring-boot-starter-test
'Starter'将其引入。
如果尚未使用spring-test
模块,则应先阅读 Spring Framework 参考文档的relevant section。
41.3 测试 Spring Boot 应用程序
Spring Boot 应用程序只是 Spring ApplicationContext
,因此除了使用普通的 Spring 上下文进行测试之外,无需执行任何其他非常特殊的操作即可对其进行测试。不过要注意的一件事是,如果使用SpringApplication
创建它,则默认情况下仅在上下文中安装 Spring Boot 的外部属性,日志记录和其他功能。
Spring Boot 提供了@SpringBootTest
注解,当您需要 Spring Boot 功能时,可以将其用作标准spring-test
@ContextConfiguration
注解的替代方法。Comments 通过SpringApplication
创建在测试中使用的ApplicationContext
起作用。
您可以使用@SpringBootTest
的webEnvironment
属性来进一步完善测试的运行方式:
-
MOCK
—加载WebApplicationContext
并提供模拟 servlet 环境。使用此 Comments 时,不会启动嵌入式 Servlet 容器。如果 servlet API 不在您的 Classpath 中,则此模式将透明地回退到创建常规的非 WebApplicationContext
。可以与@AutoConfigureMockMvc
结合使用,以对您的应用程序进行基于MockMvc
的测试。 -
RANDOM_PORT
—加载EmbeddedWebApplicationContext
并提供真实的 servlet 环境。启动嵌入式 Servlet 容器并在随机端口上侦听。 -
DEFINED_PORT
—加载EmbeddedWebApplicationContext
并提供真实的 servlet 环境。启动嵌入式 Servlet 容器并在已定义的端口上侦听(即从application.properties
或默认端口8080
)。 -
NONE
—使用SpringApplication
加载ApplicationContext
,但是不提供* any * servlet 环境(模拟或其他方式)。
Note
如果您的测试是@Transactional
,则默认情况下它将在每个测试方法的末尾回滚事务。但是,由于将这种安排与RANDOM_PORT
或DEFINED_PORT
一起使用隐式提供了一个真实的 servlet 环境,因此 HTTPClient 端和服务器将在单独的线程中运行,从而在单独的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。
Note
除了@SpringBootTest
之外,还提供了许多其他注解,用于测试应用程序的更特定的部分。有关详情,请参见下文。
Tip
别忘了还要在测试中添加@RunWith(SpringRunner.class)
,否则 Comments 将被忽略。
41.3.1 检测测试配置
如果您熟悉 Spring Test Framework,则可能会习惯使用@ContextConfiguration(classes=…)
来指定要加载哪个 Spring @Configuration
。另外,您可能经常在测试中使用嵌套的@Configuration
类。
在测试 Spring Boot 应用程序时,通常不需要这样做。只要您没有明确定义,Spring Boot 的@*Test
注解就会自动搜索您的主要配置。
搜索算法从包含测试的程序包开始工作,直到找到带有@SpringBootApplication
或@SpringBootConfiguration
Comments 的类。只要您以合理的方式拥有结构化代码,通常就可以找到您的主要配置。
Note
如果您使用测试 Comments 以测试应用程序的更具体部分进行此类设置,则应避免添加针对主应用程序类上特定区域的配置。
如果要自定义主要配置,则可以使用嵌套的@TestConfiguration
类。与将使用嵌套的@Configuration
类代替应用程序的主要配置不同的是,除了应用程序的主要配置之外,还将使用嵌套的@TestConfiguration
类。
Note
Spring 的测试框架将在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论如何发现),加载上下文的潜在耗时过程将只发生一次。
41.3.2 排除测试配置
如果您的应用程序使用组件扫描,例如,如果使用@SpringBootApplication
或@ComponentScan
,则可能会偶然发现到处都是为特定测试而创建的顶级配置类。
正如我们看过上面,@TestConfiguration
可以在测试的内部类上使用以自定义主要配置。当放在顶级类上时,@TestConfiguration
指示src/test/java
中的类不应通过扫描来拾取。然后,您可以在需要的位置显式导入该类:
@RunWith(SpringRunner.class)
@SpringBootTest
@Import(MyTestsConfiguration.class)
public class MyTests {
@Test
public void exampleTest() {
...
}
}
Note
如果您直接使用@ComponentScan
(即不通过@SpringBootApplication
),则需要向其注册TypeExcludeFilter
。有关详情,请参见the Javadoc。
41.3.3 使用随机端口
如果您需要启动运行正常的服务器进行测试,建议您使用随机端口。如果您使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
,则每次运行测试时都会随机选择一个可用端口。
@LocalServerPort
注解可用于注入实际使用的端口进入您的测试。为了方便起见,需要对启动的服务器进行 REST 调用的测试可以另外@Autowire
和TestRestTemplate
来解析到正在运行的服务器的相对链接。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortExampleTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void exampleTest() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
41.3.4 模拟和 Spybean
在运行测试时,有时有必要在应用程序上下文中模拟某些组件。例如,您可能在开发过程中无法使用某些远程服务的外观。当您要模拟在实际环境中可能难以触发的故障时,模拟功能也很有用。
Spring Boot 包含@MockBean
注解,可用于为ApplicationContext
中的 bean 定义 Mockito 模拟。您可以使用注解添加新的 bean,或替换单个现有的 bean 定义。注解可以直接用于测试类,测试中的字段或@Configuration
类和字段。在字段上使用时,还将注入创建的模拟的实例。每种测试方法后,模拟 bean 都会自动重置。
Note
只要您的测试使用 Spring Boot 的测试 Comments 之一(即@SpringBootTest
),就会自动启用此功能。要以其他方式使用此功能,需要显式添加侦听器:
@TestExecutionListeners(MockitoTestExecutionListener.class)
这是一个典型示例,其中我们用模拟实现替换了现有的RemoteService
bean:
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;
import org.springframework.test.context.junit4.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
@MockBean
private RemoteService remoteService;
@Autowired
private Reverser reverser;
@Test
public void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}
}
另外,您还可以使用@SpyBean
使用 Mockito spy
包装任何现有的 bean。有关完整的详细信息,请参见 Javadoc。
41.3.5 自动配置的测试
Spring Boot 的自动配置系统适用于应用程序,但有时对于测试而言可能有点过多。通常,仅加载测试应用程序“切片”所需的配置部分通常会很有帮助。例如,您可能想测试 Spring MVC 控制器是否正确 Map 了 URL,并且您不想在这些测试中涉及数据库调用。或者您可能想要*测试 JPA 实体,并且在运行这些测试时您对 Web 层不感兴趣。
spring-boot-test-autoconfigure
模块包含许多 Comments,可用于自动配置此类“切片”。它们中的每一个都以类似的方式工作,提供了一个@…Test
注解,该注解加载了ApplicationContext
和一个或多个@AutoConfigure…
注解,这些注解可用于自定义自动配置设置。
Note
每个片都加载非常有限的一组自动配置类。如果您需要排除其中之一,则大多数@…Test
Comments 都提供excludeAutoConfiguration
属性。或者,您可以使用@ImportAutoConfiguration#exclude
。
Tip
也可以将@AutoConfigure…
Comments 与标准@SpringBootTest
Comments 一起使用。如果您不希望对应用程序进行“切片”,但需要一些自动配置的测试 bean,则可以使用此组合。
41.3.6 自动配置的 JSON 测试
要测试对象 JSON 序列化和反序列化是否按预期工作,可以使用@JsonTest
注解。 @JsonTest
将自动配置 Jackson ObjectMapper
,任何@JsonComponent
bean 和任何 Jackson Modules
。如果您正巧用Gson
代替 Jackson 或与 Jackson 一样,它也会配置Gson
。如果需要配置自动配置的元素,则可以使用@AutoConfigureJsonTesters
Comments。
Spring Boot 包含基于 AssertJ 的助手,这些助手与 JSONassert 和 JsonPath 库一起使用以检查 JSON 是否符合预期。 JacksonTester
,GsonTester
和BasicJsonTester
类可以分别用于 Jackson,Gson 和 Strings。使用@JsonTest
时,测试类上的任何帮助程序字段都可以为@Autowired
。
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.json.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.json.*;
import org.springframework.test.context.junit4.*;
import static org.assertj.core.api.Assertions.*;
@RunWith(SpringRunner.class)
@JsonTest
public class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
public void testSerialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make")
.isEqualTo("Honda");
}
@Test
public void testDeserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content))
.isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
Note
JSON 帮助程序类也可以直接在标准单元测试中使用。如果您不使用@JsonTest
,只需在您的@Before
方法中调用帮助程序的initFields
方法。
@JsonTest
启用的自动配置的列表可以为见附录。
41.3.7 自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否按预期工作,可以使用@WebMvcTest
Comments。 @WebMvcTest
将自动配置 Spring MVC 基础结构,并将扫描的 bean 限制为@Controller
,@ControllerAdvice
,@JsonComponent
,Filter
,WebMvcConfigurer
和HandlerMethodArgumentResolver
。使用此 Comments 时,不会扫描常规@Component
bean。
@WebMvcTest
通常会被限制为单个控制器,并与@MockBean
结合使用以为所需的协作者提供模拟实现。
@WebMvcTest
也会自动配置MockMvc
。 Mock MVC 提供了一种强大的方法来快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。
Tip
您还可以通过用@AutoConfigureMockMvc
Comments 非@WebMvcTest
(例如SpringBootTest
)来自动配置MockMvc
。
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest(UserVehicleController.class)
public class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
public void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
}
}
Tip
如果您需要配置自动配置的元素(例如,当应该应用 servlet 过滤器时),则可以使用@AutoConfigureMockMvc
注解中的属性。
如果使用 HtmlUnit 或 Selenium,则自动配置还将提供WebClient
bean 和/或WebDriver
bean。这是使用 HtmlUnit 的示例:
import com.gargoylesoftware.htmlunit.*;
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
@RunWith(SpringRunner.class)
@WebMvcTest(UserVehicleController.class)
public class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
public void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
Note
默认情况下,Spring Boot 会将WebDriver
bean 放入特殊的“作用域”中,以确保驱动程序在每次测试后均退出,并确保注入了新实例。如果您不希望出现这种情况,可以将@Scope("singleton")
添加到WebDriver
@Bean
定义中。
@WebMvcTest
启用的自动配置的列表可以为见附录。
41.3.8 自动配置的数据 JPA 测试
如果要测试 JPA 应用程序,可以使用@DataJpaTest
。默认情况下,它将配置一个内存嵌入式数据库,扫描@Entity
类并配置 Spring Data JPA 存储库。常规@Component
bean 将不会加载到ApplicationContext
中。
默认情况下,数据 JPA 测试是事务性的,并且在每个测试结束时都会回滚,有关更多详细信息,请参见 Spring 参考文档中的relevant section。如果这不是您想要的,则可以按以下方式禁用测试或整个类的事务 Management:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringRunner.class)
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {
}
数据 JPA 测试也可以注入TestEntityManager bean,以替代专门为测试设计的标准 JPA EntityManager
。如果要在@DataJpaTests
之外使用TestEntityManager
,也可以使用@AutoConfigureTestEntityManager
Comments。如果需要,也可以提供JdbcTemplate
。
import org.junit.*;
import org.junit.runner.*;
import org.springframework.boot.test.autoconfigure.orm.jpa.*;
import static org.assertj.core.api.Assertions.*;
@RunWith(SpringRunner.class)
@DataJpaTest
public class ExampleRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
public void testExample() throws Exception {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("1234");
}
}
内存嵌入式数据库通常运行良好,不需要安装任何开发人员,因此通常可以很好地进行测试。但是,如果您希望对真实数据库运行测试,则可以使用@AutoConfigureTestDatabase
注解:
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class ExampleRepositoryTests {
// ...
}
@DataJpaTest
启用的自动配置的列表可以为见附录。
41.3.9 自动配置的 JDBC 测试
@JdbcTest
与@DataJpaTest
类似,但适用于与 jdbc 相关的测试。默认情况下,它还将配置一个内存嵌入式数据库和一个JdbcTemplate
。常规@Component
bean 将不会加载到ApplicationContext
中。
缺省情况下,JDBC 测试是事务性的,并且在每个测试结束时都会回滚,有关更多详细信息,请参见 Spring 参考文档中的relevant section。如果这不是您想要的,则可以按以下方式禁用测试或整个类的事务 Management:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringRunner.class)
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {
}
如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase
注解,就像使用DataJpaTest
一样。
@JdbcTest
启用的自动配置的列表可以为见附录。
41.3.10 自动配置的 Data MongoDB 测试
如果要测试 MongoDB 应用程序,可以使用@DataMongoTest
。默认情况下,它将配置内存嵌入式 MongoDB(如果可用),配置MongoTemplate
,扫描@Document
类并配置 Spring Data MongoDB 存储库。常规@Component
bean 将不会加载到ApplicationContext
中:
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@DataMongoTest
public class ExampleDataMongoTests {
@Autowired
private MongoTemplate mongoTemplate;
//
}
内存嵌入式 MongoDB 通常运行良好,不需要安装任何开发人员,因此通常可以很好地用于测试。但是,如果您希望对真实的 MongoDB 服务器运行测试,则应排除嵌入式 MongoDB 自动配置:
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class ExampleDataMongoNonEmbeddedTests {
}
@DataMongoTest
启用的自动配置的列表可以为见附录。
41.3.11 自动配置的 RESTClient 端
如果要测试 RESTClient 端,可以使用@RestClientTest
注解。默认情况下,它将自动配置 Jackson 和 GSON 支持,配置RestTemplateBuilder
并添加对MockRestServiceServer
的支持。要测试的特定 bean 应该使用@RestClientTest
的value
或components
属性指定:
@RunWith(SpringRunner.class)
@RestClientTest(RemoteVehicleDetailsService.class)
public class ExampleRestClientTest {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
public void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
throws Exception {
this.server.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
@RestClientTest
启用的自动配置的列表可以为见附录。
41.3.12 自动配置的 Spring REST Docs 测试
如果要在测试中使用 Spring REST Docs,则可以使用@AutoConfigureRestDocs
注解。它将自动配置MockMvc
以使用 Spring REST Docs,并不需要 Spring REST Docs 的 JUnit 规则。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs("target/generated-snippets")
public class UserDocumentationTests {
@Autowired
private MockMvc mvc;
@Test
public void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
@AutoConfigureRestDocs
除了配置输出目录外,还可以配置将出现在任何记录的 URI 中的主机,方案和端口。如果您需要对 Spring REST Docs 的配置进行更多控制,则可以使用RestDocsMockMvcConfigurationCustomizer
bean:
@TestConfiguration
static class CustomizationConfiguration
implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
如果要使用 Spring REST Docs 对参数化输出目录的支持,可以创建RestDocumentationResultHandler
bean。自动配置将使用此结果处理程序调用alwaysDo
,从而使每个MockMvc
调用自动生成默认代码段:
@TestConfiguration
static class ResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
41.3.13 用户配置和切片
如果您以合理的方式拥有结构化代码,则您的@SpringBootApplication
类为默认使用作为测试的配置。
因此,重要的是不要用特定于其功能特定区域的配置来乱扔应用程序的主类。
假设您使用的是 Spring Batch,并且依赖于自动配置。您可以如下定义您的@SpringBootApplication
:
@SpringBootApplication
@EnableBatchProcessing
public class SampleApplication { ... }
因为此类是测试的源配置,所以任何切片测试实际上都将尝试启动 Spring Batch,这绝对不是您想要执行的操作。建议的方法是将特定于区域的配置移到与应用程序相同级别的单独的@Configuration
类。
@Configuration
@EnableBatchProcessing
public class BatchConfiguration { ... }
Note
根据您应用程序的表面积,您可以为您的自定义设置一个ApplicationConfiguration
类,也可以在每个域区域一个类。后一种方法允许您在必要时通过@Import
在一个测试中启用它。
混乱的另一个来源是 Classpath 扫描。假设在以合理的方式组织代码的同时,您需要扫描其他程序包。您的应用程序可能如下所示:
@SpringBootApplication
@ComponentScan({ "com.example.app", "org.acme.another" })
public class SampleApplication { ... }
这有效地覆盖了默认的组件扫描指令,并具有扫描这两个程序包的副作用,而与您选择的切片无关。例如,@DataJpaTest
将突然扫描您的应用程序的组件和用户配置。同样,将自定义指令移至单独的类是解决此问题的好方法。
Tip
如果这不是您的选择,则可以在测试层次结构中的某个位置创建@SpringBootConfiguration
,以便代替它使用。或者,您可以为测试指定一个源,这将禁用查找默认源的行为。
41.3.14 使用 Spock 测试 Spring Boot 应用程序
如果您希望使用 Spock 测试 Spring Boot 应用程序,则应在应用程序的构建中添加对 Spock 的spock-spring
模块的依赖。 spock-spring
将 Spring 的测试框架集成到了 Spock 中。究竟如何使用 Spock 测试 Spring Boot 应用程序取决于您所使用的 Spock 版本。
Note
Spring Boot 为 Spock 1.0 提供了依赖 Management。如果您想使用 Spock 1.1,则应在build.gradle
或pom.xml
文件中覆盖 spock.version 属性。
使用 Spock 1.1 时,只能使用注解described above,并且可以使用@SpringBootTest
CommentsSpecification
以满足测试的需要。
使用 Spock 1.0 时,@SpringBootTest
不适用于 Web 项目。您需要使用@SpringApplicationConfiguration
和@WebIntegrationTest(randomPort = true)
。无法使用@SpringBootTest
意味着您还会丢失自动配置的TestRestTemplate
bean。您可以使用以下配置自行创建等效的 bean:
@Configuration
static class TestRestTemplateConfiguration {
@Bean
public TestRestTemplate testRestTemplate(
ObjectProvider<RestTemplateBuilder> builderProvider,
Environment environment) {
RestTemplateBuilder builder = builderProvider.getIfAvailable();
TestRestTemplate template = builder == null ? new TestRestTemplate()
: new TestRestTemplate(builder.build());
template.setUriTemplateHandler(new LocalHostUriTemplateHandler(environment));
return template;
}
}
41.4 测试 Util
一些测试 Util 类被打包为spring-boot
的一部分,通常在测试您的应用程序时很有用。
41.4.1 ConfigFileApplicationContextInitializer
ConfigFileApplicationContextInitializer
是ApplicationContextInitializer
,可以应用于测试以加载 Spring Boot application.properties
文件。当您不需要@SpringBootTest
提供的全部功能时,可以使用此功能。
@ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
Note
单独使用ConfigFileApplicationContextInitializer
不会为@Value("${…}")
注入提供支持。它唯一的工作就是确保将application.properties
文件加载到 Spring 的Environment
中。要获得@Value
支持,您需要另外配置PropertySourcesPlaceholderConfigurer
或使用@SpringBootTest
,其中将自动为您配置一个。
41.4.2 EnvironmentTestUtils
EnvironmentTestUtils
可让您快速将属性添加到ConfigurableEnvironment
或ConfigurableApplicationContext
。只需使用key=value
字符串调用它即可:
EnvironmentTestUtils.addEnvironment(env, "org=Spring", "name=Boot");
41.4.3 OutputCapture
OutputCapture
是一个 JUnit Rule
,可用于捕获System.out
和System.err
输出。只需将捕获声明为@Rule
,然后将toString()
用于 assert:
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"));
}
}
41.4.4 TestRestTemplate
TestRestTemplate
是 Spring RestTemplate
的便捷替代方法,在集成测试中非常有用。您可以使用普通模板或发送基本 HTTP 身份验证(带有用户名和密码)的模板。在这两种情况下,模板都不会通过在服务器端错误上引发异常来以易于测试的方式运行。建议(但不是强制性的)使用 Apache HTTP Client(版本 4.3.2 或更高版本),如果您在 Classpath 中使用了它,则TestRestTemplate
将通过适当配置 Client 端来响应。如果您确实使用 Apache 的 HTTPClient 端,则会启用一些其他易于测试的功能:
-
重定向不会被执行(因此您可以声明响应位置)
-
Cookies 将被忽略(因此模板是 Stateless 的)
TestRestTemplate
可以直接在集成测试中实例化:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
@Test
public void testRequest() throws Exception {
HttpHeaders headers = template.getForEntity("http://myhost.com/example", String.class).getHeaders();
assertThat(headers.getLocation().toString(), containsString("myotherhost"));
}
}
另外,如果您将@SpringBootTest
Comments 与WebEnvironment.RANDOM_PORT
或WebEnvironment.DEFINED_PORT
一起使用,则只需注入一个完整配置的TestRestTemplate
并开始使用它。如有必要,可以通过RestTemplateBuilder
bean 应用其他定制。任何未指定主机和端口的 URL 将自动连接到嵌入式服务器:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {
@Autowired
private TestRestTemplate template;
@Test
public void testRequest() throws Exception {
HttpHeaders headers = template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation().toString(), containsString("myotherhost"));
}
@TestConfiguration
static class Config {
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.additionalMessageConverters(...)
.customizers(...);
}
}
}