目录

4.23 测试

优质
小牛编辑
126浏览
2023-12-01

Spring Boot提供了许多实用程序和注释来帮助您测试应用程序。 测试支持由两个模块提供:spring-boot-test包含核心项,spring-boot-test-autoconfigure支持测试的自动配置。

大多数开发人员使用spring-boot-starter-test的“Starter”,它导入Spring Boot测试模块以及JUnit,AssertJ,Hamcrest和许多其他有用的库。

4.24.1 测试范围依赖项

spring-boot-starter-test“Starter”(在测试范围内)包含以下提供的库:

  • JUnit:单元测试Java应用程序的事实上的标准。
  • Spring Test和Spring Boot测试:Spring Boot应用程序的实用程序和集成测试支持。
  • AssertJ:一个Stream的断言库。
  • Hamcrest:匹配器对象库(也称为约束或谓词)。
  • Mockito:一个Java模拟框架。
  • JSONassert:JSON的断言库。
  • JsonPath:JSON的XPath。

我们通常发现这些常用库在编写测试时很有用。 如果这些库不符合您的需求,您可以添加自己的其他测试依赖项。

4.24.2 测试Spring应用程序

依赖注入的一个主要优点是它应该使您的代码更容易进行单元测试。 您可以使用new运算符实例化对象,甚至不涉及Spring。 您还可以使用模拟对象而不是真正的依赖项。

通常,您需要越过单元测试并开始集成测试(使用Spring ApplicationContext)。 能够在不需要部署应用程序或需要连接到其他基础架构的情况下执行集成测试非常有用。

Spring Framework包含一个用于此类集成测试的专用测试模块。 您可以直接向org.springframework声明一个依赖项:spring-test或使用spring-boot-starter-test“Starter”来传递它。

如果您之前没有使用过spring-test模块,那么首先应阅读Spring Framework参考文档的相关部分。

4.24.3 测试Spring Boot应用程序

Spring Boot应用程序是一个Spring ApplicationContext,因此除了通常使用的Spring上下文之外,还没有什么特别的东西可以用来测试它。

只有在使用SpringApplication创建Spring Boot的外部属性,日志记录和其他功能时,才会默认安装在上下文中.

Spring Boot提供了一个@SpringBootTest注释,当您需要Spring Boot功能时,它可以用作标准spring-test @ContextConfiguration注释的替代方法。 注释通过SpringApplication创建测试中使用的ApplicationContext来工作。 除了@SpringBootTest之外,还提供了许多其他注释来测试应用程序的更具体的切片。

如果您使用的是JUnit 4,请不要忘记将@RunWith(SpringRunner.class)添加到测试中,否则将忽略注释。 如果您使用的是JUnit 5,则无需将等效的@ExtendWith(SpringExtension)添加为@SpringBootTest而另一个@ ...Test注释已经使用它进行了注释.

默认情况下,@ SpringBootTest不会启动服务器。 您可以使用@SpringBootTest的webEnvironment属性来进一步优化测试的运行方式:

  • MOCK(默认):加载Web ApplicationContext并提供模拟Web环境。 使用此批注时,不会启动嵌入式服务器。 如果类路径上没有Web环境,则此模式将透明地回退到创建常规非Web ApplicationContext。 它可以与@AutoConfigureMockMvc或@AutoConfigureWebTestClient结合使用,以进行基于模拟的Web应用程序测试。
  • RANDOM_PORT:加载WebServerApplicationContext并提供真实的Web环境。 嵌入式服务器启动并在随机端口上侦听。
  • DEFINED_PORT:加载WebServerApplicationContext并提供真实的Web环境。 嵌入式服务器启动并侦听定义的端口(来自您的application.properties)或默认端口8080。
  • NONE:使用SpringApplication加载ApplicationContext,但不提供任何Web环境(模拟或其他)。

如果您的测试是@Transactional,则默认情况下会在每个测试方法的末尾回滚事务。 但是,由于使用RANDOM_PORT或DEFINED_PORT这种安排隐式提供了一个真正的servlet环境,因此HTTP客户端和服务器在不同的线程中运行,因此在单独的事务中运行。 在这种情况下,在服务器上启动的任何事务都不会回滚.

带有webEnvironment = WebEnvironment.RANDOM_PORT的@SpringBootTest也将在一个单独的随机端口上启动管理服务器,如果您的应用程序使用不同的管理服务器端口.

4.24.3.1 检测Web应用程序类型

如果Spring MVC可用,则配置基于MVC的常规应用程序上下文。 如果您只有Spring WebFlux,我们会检测到并配置基于WebFlux的应用程序上下文。

如果两者都存在,则Spring MVC优先。 如果要在此方案中测试响应式Web应用程序,则必须设置spring.main.web-application-type属性:

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
public class MyWebFluxTests { ... }

4.24.3.2 检测测试配置

如果您熟悉Spring Test Framework,则可能习惯使用@ContextConfiguration(classes = ...)来指定要加载的Spring @Configuration。 或者,您可能经常在测试中使用嵌套的@Configuration类。

在测试Spring Boot应用程序时,通常不需要这样做。 Spring Boot的@*Test注解会在您未明确定义主要配置时自动搜索主要配置。

搜索算法从包含测试的包开始工作,直到找到使用@SpringBootApplication或@SpringBootConfiguration注释的类。 只要您以合理的方式构建代码,通常就会找到主要配置。

如果使用测试批注来测试应用程序的更具体的片段,则应避免在main方法的应用程序类中添加特定于特定区域的配置设置。. @SpringBootApplication的基础组件扫描配置定义了排除过滤器,用于确保切片按预期工作。 如果在@SpringBootApplication注释的类上使用显式的@ComponentScan指令,请注意将禁用这些过滤器。 如果您正在使用切片,则应再次定义它们.

如果要自定义主要配置,可以使用嵌套的@TestConfiguration类。 与嵌套的@Configuration类不同,它将用于代替应用程序的主要配置,除了应用程序的主要配置之外,还使用嵌套的@TestConfiguration类。

Spring的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(无论如何发现),加载上下文的潜在耗时过程只会发生一次.

4.24.3.3 排除测试配置

如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication或@ComponentScan),您可能会发现仅为特定测试创建的顶级配置类偶然会在任何地方被拾取。

正如我们之前所见,@TestConfiguration可用于测试的内部类以自定义主要配置。 当置于顶级类时,@TestConfiguration指示不应通过扫描拾取src/test/java中的类。 然后,您可以在需要的位置显式导入该类,如以下示例所示:

@RunWith(SpringRunner.class)
@SpringBootTest
@Import(MyTestsConfiguration.class)
public class MyTests {

	@Test
	public void exampleTest() {
		...
	}

}

如果直接使用@ComponentScan(即不通过@SpringBootApplication),则需要使用它注册TypeExcludeFilter。 有关详细信息,请参阅Javadoc.

4.24.3.4 使用模拟环境进行测试

默认情况下,@SpringBootTest不会启动服务器。 如果您要针对此模拟环境测试Web端点,则可以另外配置MockMvc,如以下示例所示:

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcExampleTests {

	@Autowired
	private MockMvc mvc;

	@Test
	public void exampleTest() throws Exception {
		this.mvc.perform(get("/")).andExpect(status().isOk())
				.andExpect(content().string("Hello World"));
	}

}

如果您只想关注Web层而不是启动完整的ApplicationContext,请考虑使用@WebMvcTest.

或者,您可以配置WebTestClient,如以下示例所示:

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWebTestClient
public class MockWebTestClientExampleTests {

	@Autowired
	private WebTestClient webClient;

	@Test
	public void exampleTest() {
		this.webClient.get().uri("/").exchange().expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello World");
	}

}

4.24.3.5 使用正在运行的服务器进行测试

如果您需要启动一个完整运行的服务器,我们建议您使用随机端口。 如果使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT),则每次运行测试时都会随机选取一个可用端口。

@LocalServerPort注释可用于注入测试中使用的实际端口。 为方便起见,需要对启动的服务器进行REST调用的测试还可以@Autowire WebTestClient,它解析与正在运行的服务器的相对链接,并附带用于验证响应的专用API,如以下示例所示:

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.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortWebTestClientExampleTests {

	@Autowired
	private WebTestClient webClient;

	@Test
	public void exampleTest() {
		this.webClient.get().uri("/").exchange().expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello World");
	}

}

此设置需要类路径上的spring-webflux。 如果你不能或不会添加webflux,Spring Boot还提供了一个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 RandomPortTestRestTemplateExampleTests {

	@Autowired
	private TestRestTemplate restTemplate;

	@Test
	public void exampleTest() {
		String body = this.restTemplate.getForObject("/", String.class);
		assertThat(body).isEqualTo("Hello World");
	}

}

4.24.3.6 使用JMX

当测试上下文框架缓存上下文时,默认情况下禁用JMX以防止相同的组件在同一域上注册。 如果此类测试需要访问MBeanServer,请考虑将其标记为dirty:

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
public class SampleJmxTests {

	@Autowired
	private MBeanServer mBeanServer;

	@Test
	public void exampleTest() {
		// ...
	}

}

4.24.3.7 Mocking和Spying Beans

运行测试时,有时需要在应用程序上下文中模拟某些组件。 例如,您可能拥有在开发期间不可用的某些远程服务的外观。 当您想要模拟在真实环境中难以触发的故障时,模拟也很有用。

Spring Boot包含一个@MockBean注释,可用于为ApplicationContext中的bean定义Mockito模拟。 您可以使用批注添加新bean或替换单个现有bean定义。 注释可以直接用于测试类,测试中的字段或@Configuration类和字段。 在字段上使用时,也会注入创建的模拟的实例。 每种测试方法后,模拟Bean都会自动重置。

如果您的测试使用Spring Boot的测试注释之一(例如@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将任何现有bean包装为Mockito间谍。 有关详细信息,请参阅Javadoc。

虽然Spring的测试框架在测试之间缓存应用程序上下文并重用共享相同配置的测试的上下文,但使用@MockBean或@SpyBean会影响缓存键,这很可能会增加上下文的数量.

如果您使用@SpyBean监视具有按名称引用参数的@Cacheable方法的bean,则必须使用-parameters编译应用程序。 这确保了一旦bean被监视,参数名称可用于缓存基础结构.

4.24.3.8 自动配置测试

Spring Boot的自动配置系统适用于应用程序,但有时对于测试来说有点太多了。 通常有助于仅加载测试应用程序“切片”所需的配置部分。 例如,您可能希望测试Spring MVC控制器是否正确映射URL,并且您不希望在这些测试中涉及数据库调用,或者您可能想要测试运行JPA实体,并且您对Web层不感兴趣 。

spring-boot-test-autoconfigure模块包括许多可用于自动配置这种“切片”的注释。 它们中的每一个都以类似的方式工作,提供一个@...Test注释,用于加载ApplicationContext和一个或多个@AutoConfigure...注释,可用于自定义自动配置设置。

每个切片将组件扫描限制为适当的组件,并加载一组非常有限的自动配置类。 如果您需要排除其中一个,大多数@...Test注释提供excludeAutoConfiguration属性。 或者,您可以使用@ImportAutoConfiguration#exclude.

不支持在一个测试中使用通过使用多个@...Test注释包括多个“切片”。 如果您需要多个“切片”,请选择其中一个@...Testannotations并手动包含@ AutoConfigure...其他“切片”的注释.

也可以将@AutoConfigure...注释与标准@SpringBootTest注释一起使用。 如果您对“切片”应用程序不感兴趣但想要一些自动配置的测试bean,则可以使用此组合.

4.24.3.9 自动配置的JSON测试

要测试该对象JSON序列化和反序列化是否按预期工作,您可以使用@JsonTest批注。 @JsonTest自动配置可用的受支持JSON映射器,它可以是以下库之一:

  • Jackson ObjectMapper,任何@JsonComponent bean和任何Jackson Modules
  • Gson
  • Jsonb

可以在附录中找到由@JsonTest启用的自动配置列表。

如果需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters注释。

Spring Boot包括基于AssertJ的助手,它们与JSONAssert和JsonPath库一起使用,以检查JSON是否按预期显示。 JacksonTester,GsonTester,JsonbTester和BasicJsonTester类可分别用于Jackson,Gson,Jsonb和Strings。 使用@JsonTest时,测试类上的任何辅助字段都可以是@Autowired。 以下示例显示了Jackson的测试类:

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");
	}

}

JSON帮助程序类也可以直接在标准单元测试中使用。 为此,如果不使用@JsonTest,请在@Before方法中调用助手的initFields方法。

4.24.3.10 自动配置的Spring MVC测试

要测试Spring MVC控制器是否按预期工作,请使用@WebMvcTest注释。 @WebMvcTest自动配置Spring MVC基础结构并将扫描的bean限制为@Controller,@ControllerAdvice,@JsonComponent,Converter,GenericConverter,Filter,WebMvcConfigurer和HandlerMethodArgumentResolver。 使用此批注时,不会扫描常规@Component bean。

可以在附录中找到由@WebMvcTest启用的自动配置设置列表。

如果需要注册额外的组件,例如Jackson Module,则可以在测试中使用@Import导入其他配置类。

通常,@WebMvcTest仅限于单个控制器,并与@MockBean结合使用,为所需的协作者提供模拟实现。

@WebMvcTest还自动配置MockMvc。 Mock MVC提供了一种快速测试MVC控制器的强大方法,无需启动完整的HTTP服务器。

您还可以通过使用@AutoConfigureMockMvc对其进行注释,在非@WebMvcTest(例如@SpringBootTest)中自动配置MockMvc。 以下示例使用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"));
	}

}

如果需要配置自动配置的元素(例如,应该应用servlet过滤器时),可以使用@AutoConfigureMockMvc注释中的属性。

如果使用HtmlUnit或Selenium,则自动配置还提供HTMLUnit 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");
	}

}

默认情况下,Spring Boot将WebDriver bean放在一个特殊的“scope”中,以确保驱动程序在每次测试后退出并注入新实例。 如果您不想要此行为,可以将@Scope("singleton")添加到WebDriver @Bean定义中。

Spring Boot创建的webDriver范围将替换任何用户定义的同名范围。 如果您定义自己的webDriver范围,则可能会在使用@WebMvcTest时发现它停止工作。

如果在类路径上有Spring Security,@ WebMvcTest也将扫描WebSecurityConfigurer bean。 您可以使用Spring Security的测试支持,而不是完全禁用此类测试的安全性。 有关如何使用Spring Security的MockMvc支持的更多详细信息,请参见第9.5 “使用Spring Security测试操作方法”部分。

有时编写Spring MVC测试是不够的; Spring Boot可以帮助您使用实际服务器运行完整的端到端测试。

4.24.3.11 自动配置的Spring WebFlux测试

要测试Spring WebFlux控制器是否按预期工作,可以使用@WebFluxTest批注。 @WebFluxTest自动配置Spring WebFlux基础结构并将扫描的bean限制为@Controller,@ControllerAdvice,@JsonComponent,Converter,GenericConverter和WebFluxConfigurer。 使用@WebFluxTest批注时,不会扫描常规@Component bean。

可以在附录中找到由@WebFluxTest启用的自动配置列表。

如果需要注册额外的组件,例如Jackson Module,则可以在测试中使用@Import导入其他配置类。<

通常,@WebFluxTest仅限于单个控制器,并与@MockBean批注结合使用,为所需的协作者提供模拟实现。

@WebFluxTest还自动配置WebTestClient,它提供了一种快速测试WebFlux控制器的强大方法,无需启动完整的HTTP服务器。

您还可以通过使用@AutoConfigureWebTestClient对其进行注释,在非@WebFluxTest(例如@SpringBootTest)中自动配置WebTestClient。 以下示例显示了一个同时使用@WebFluxTest和WebTestClient的类:

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

@RunWith(SpringRunner.class)
@WebFluxTest(UserVehicleController.class)
public class MyControllerTests {

	@Autowired
	private WebTestClient webClient;

	@MockBean
	private UserVehicleService userVehicleService;

	@Test
	public void testExample() throws Exception {
		given(this.userVehicleService.getVehicleDetails("sboot"))
				.willReturn(new VehicleDetails("Honda", "Civic"));
		this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN)
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Honda Civic");
	}

}

此设置仅由WebFlux应用程序支持,因为在模拟的Web应用程序中使用WebTestClient仅适用于WebFlux。

@WebFluxTest无法检测通过功能Web框架注册的路由。 要在上下文中测试RouterFunction bean,请考虑通过@Import或使用@SpringBootTest自行导入RouterFunction。

有时编写Spring WebFlux测试是不够的; Spring Boot可以帮助您使用实际服务器运行完整的端到端测试。

4.24.3.12 自动配置的Data JPA测试

您可以使用@DataJpaTest批注来测试JPA应用程序。 默认情况下,它配置内存中的嵌入式数据库,扫描@Entity类,并配置Spring Data JPA存储库。 常规@Component bean未加载到ApplicationContext中。

可以在附录中找到由@DataJpaTest启用的自动配置设置列表。

默认情况下,数据JPA测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅Spring Framework Reference Documentation中的相关部分。 如果这不是您想要的,您可以为测试或整个类禁用事务管理,如下所示:

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的替代方法。 如果要在@DataJpaTest实例之外使用TestEntityManager,还可以使用@AutoConfigureTestEntityManager注释。 如果需要,还可以使用JdbcTemplate。 以下示例显示正在使用的@DataJpaTest批注:

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 {

	// ...

}

4.24.3.13 自动配置的JDBC测试

@JdbcTest类似于@DataJpaTest,但适用于仅需要DataSource且不使用Spring Data JDBC的测试。 默认情况下,它配置内存中的嵌入式数据库和JdbcTemplate。 常规@Component bean未加载到ApplicationContext中。

可以在附录中找到由@JdbcTest启用的自动配置列表。

默认情况下,JDBC测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅Spring Framework Reference Documentation中的相关部分。 如果这不是您想要的,您可以禁用测试或整个类的事务管理,如下所示:

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 {

}

如果您希望测试针对真实数据库运行,则可以使用与DataJpaTest相同的方式使用@AutoConfigureTestDatabase批注。 (参见“第4.24.3.12节”,“自动配置的数据JPA测试”。)

4.24.3.14 自动配置的Data JDBC测试

@DataJdbcTest类似于@JdbcTest,但适用于使用Spring Data JDBC存储库的测试。 默认情况下,它配置内存中嵌入式数据库,JdbcTemplate和Spring Data JDBC存储库。 常规@Component bean未加载到ApplicationContext中。

可以在附录中找到由@DataJdbcTest启用的自动配置列表。

默认情况下,数据JDBC测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅Spring Framework Reference Documentation中的相关部分。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC示例中所示。

如果您希望测试针对真实数据库运行,则可以使用与DataJpaTest相同的方式使用@AutoConfigureTestDatabase批注。 (参见“第4.24.3.12节”,“自动配置的数据JPA测试”。)

4.24.3.15 自动配置的jOOQ测试

你可以像@JdbcTest一样使用@JooqTest,但是用于与jOOQ相关的测试。 由于jOOQ严重依赖于与数据库模式相对应的基于Java的模式,因此使用现有的DataSource。 如果要将其替换为内存数据库,可以使用@AutoConfigureTestDatabase覆盖这些设置。 (有关在Spring Boot中使用jOOQ的更多信息,请参阅本章前面的“第4.8.6节”,“使用jOOQ”。)常规@Component bean未加载到ApplicationContext中。

可以在附录中找到由@JooqTest启用的自动配置列表。

@JooqTest配置DSLContext。 常规@Component bean未加载到ApplicationContext中。 以下示例显示了正在使用的@JooqTest批注:

import org.jooq.DSLContext;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@JooqTest
public class ExampleJooqTests {

	@Autowired
	private DSLContext dslContext;
}

JOOQ测试是事务性的,默认情况下在每次测试结束时回滚。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC示例中所示。

4.24.3.16 自动配置的Data MongoDB测试

您可以使用@DataMongoTest来测试MongoDB应用程序。 默认情况下,它配置内存中嵌入的MongoDB(如果可用),配置MongoTemplate,扫描@Document类,以及配置Spring Data MongoDB存储库。 常规@Component bean未加载到ApplicationContext中。 (有关将MongoDB与Spring Boot一起使用的更多信息,请参阅本章前面的“第4.9.2节”,“MongoDB”。)

可以在附录中找到由@DataMongoTest启用的自动配置设置列表。

以下类显示正在使用的@DataMongoTest批注:

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 {

}

4.24.3.17 自动配置的Data Neo4j测试

您可以使用@DataNeo4jTest来测试Neo4j应用程序。 默认情况下,它使用内存中嵌入式Neo4j(如果嵌入式驱动程序可用),扫描@NodeEntity类,并配置Spring Data Neo4j存储库。 常规@Component bean未加载到ApplicationContext中。 (有关使用带有Spring Boot的Neo4J的更多信息,请参阅本章前面的“第4.9.3节”,“Neo4j”。)

可以在附录中找到由@ DataNeo4jTest启用的自动配置设置列表。

以下示例显示了在Spring Boot中使用Neo4J测试的典型设置:

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataNeo4jTest
public class ExampleDataNeo4jTests {

	@Autowired
	private YourRepository repository;

	//
}

默认情况下,Data Neo4j测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅Spring Framework Reference Documentation中的相关部分。 如果这不是您想要的,您可以禁用测试或整个类的事务管理,如下所示:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringRunner.class)
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {

}

4.24.3.18 自动配置的Data Redis测试

您可以使用@DataRedisTest来测试Redis应用程序。 默认情况下,它会扫描@RedisHash类并配置Spring Data Redis存储库。 常规@Component bean未加载到ApplicationContext中。(有关使用带有Spring Boot的Redis的更多信息,请参阅本章前面的“第4.9.1节”,“Redis”。)

可以在附录中找到由@DataRedisTest启用的自动配置设置列表。

以下示例显示正在使用的@DataRedisTest批注:

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataRedisTest
public class ExampleDataRedisTests {

	@Autowired
	private YourRepository repository;

	//
}

4.24.3.19 自动配置的Data LDAP测试

您可以使用@DataLdapTest来测试LDAP应用程序。 默认情况下,它配置内存中嵌入式LDAP(如果可用),配置LdapTemplate,扫描@Entry类,以及配置Spring Data LDAP存储库。 常规@Component bean未加载到ApplicationContext中。 (有关在Spring Boot中使用LDAP的更多信息,请参阅本章前面的“第4.9.9节”,“LDAP”。)

可以在附录中找到由@DataLdapTest启用的自动配置设置列表。

以下示例显示正在使用的@DataLdapTest批注:

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataLdapTest
public class ExampleDataLdapTests {

	@Autowired
	private LdapTemplate ldapTemplate;

	//
}

内存中嵌入式LDAP通常适用于测试,因为它速度快,不需要任何开发人员安装。 但是,如果您更喜欢针对真实LDAP服务器运行测试,则应排除嵌入式LDAP自动配置,如以下示例所示:

import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
public class ExampleDataLdapNonEmbeddedTests {

}

4.24.3.20 自动配置的REST Clients 测试

您可以使用@RestClientTest批注来测试REST客户端。 默认情况下,它会自动配置Jackson,GSON和Jsonb支持,配置RestTemplateBuilder,并添加对MockRestServiceServer的支持。 常规@Component bean未加载到ApplicationContext中。

可以在附录中找到由@RestClientTest启用的自动配置设置列表。

应使用@RestClientTest的value或components属性指定要测试的特定bean,如以下示例所示:

@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");
	}

}

4.24.3.21 自动配置的Spring REST Docs测试

您可以使用@AutoConfigureRestDocs批注在Mock MVC,REST Assured或WebTestClient的测试中使用Spring REST Docs。 它消除了对Spring REST Docs中JUnit规则的需求。

@AutoConfigureRestDocs可用于覆盖默认输出目录(如果您使用的是Maven,则使用target/generated-snippets,如果使用Gradle,则使用build/generated-snippets)。 它还可用于配置出现在任何已记录的URI中的主机,方案和端口。

1)使用Mock MVC测试自动配置的Spring REST Docs

@AutoConfigureRestDocs自定义MockMvc bean以使用Spring REST Docs。 您可以使用@Autowired注入它,并像在使用Mock MVC和Spring REST Docs时一样在测试中使用它,如以下示例所示:

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
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"));
	}

}

如果您需要对Spring REST Docs配置的更多控制,而不是@AutoConfigureRestDocs的属性,则可以使用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调用自动生成默认代码段。 以下示例显示正在定义的RestDocumentationResultHandler:

@TestConfiguration
static class ResultHandlerConfiguration {

	@Bean
	public RestDocumentationResultHandler restDocumentation() {
		return MockMvcRestDocumentation.document("{method-name}");
	}

}

2)使用REST Assured测试自动配置的Spring REST Docs

@AutoConfigureRestDocs生成一个RequestSpecification bean,预先配置为使用Spring REST Docs,可用于您的测试。 您可以使用@Autowired注入它并在测试中使用它,就像使用REST Assured和Spring REST Doc时一样,如下例所示:

import io.restassured.specification.RequestSpecification;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
public class UserDocumentationTests {

	@LocalServerPort
	private int port;

	@Autowired
	private RequestSpecification documentationSpec;

	@Test
	public void listUsers() {
		given(this.documentationSpec).filter(document("list-users")).when()
				.port(this.port).get("/").then().assertThat().statusCode(is(200));
	}

}

如果您需要对Spring REST Docs配置的更多控制,而不是@AutoConfigureRestDocs的属性,则可以使用RestDocsRestAssuredConfigurationCustomizer bean,如以下示例所示:

@TestConfiguration
public static class CustomizationConfiguration
		implements RestDocsRestAssuredConfigurationCustomizer {

	@Override
	public void customize(RestAssuredRestDocumentationConfigurer configurer) {
		configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
	}

}

4.24.3.22 额外的自动配置和切片

每个切片提供一个或多个@AutoConfigure ...注释,即定义应作为切片的一部分包含的自动配置。 可以通过创建自定义@AutoConfigure...注释或仅通过将@ImportAutoConfiguration添加到测试来添加其他自动配置,如以下示例所示:

@RunWith(SpringRunner.class)
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
public class ExampleJdbcTests {

}

确保不使用常规的@Import批注来导入自动配置,因为Spring Boot会以特定的方式处理它们。

4.24.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,以便使用它。 或者,您可以为测试指定源,这会禁用查找默认源的行为。

4.24.3.24 使用Spock来测试Spring Boot应用程序

如果您希望使用Spock来测试Spring Boot应用程序,您应该将Spock的spock-spring模块的依赖项添加到您的应用程序的构建中。 spock-spring将Spring的测试框架集成到Spock中。 建议您使用Spock 1.2或更高版本从Spock的Spring Framework和Spring Boot集成的许多改进中受益。 有关更多详细信息,请参阅Spock Spring模块的文档。

4.24.4 测试工具包

在测试应用程序时通常有用的一些测试实用程序类被打包为spring-boot的一部分。

4.24.4.1 ConfigFileApplicationContextInitializer

ConfigFileApplicationContextInitializer是一个ApplicationContextInitializer,您可以将其应用于测试以加载Spring Boot application.properties文件。 当您不需要@SpringBootTest提供的全套功能时,可以使用它,如以下示例所示:

@ContextConfiguration(classes = Config.class,
	initializers = ConfigFileApplicationContextInitializer.class)

仅使用ConfigFileApplicationContextInitializer不支持@Value("${...}")注入。 它唯一的工作是确保将application.properties文件加载到Spring的环境中。 对于@Value支持,您需要另外配置PropertySourcesPlaceholderConfigurer或使用@SpringBootTest,它会自动为您配置一个。

4.24.4.2 TestPropertyValues

TestPropertyValues允许您快速向ConfigurableEnvironment或ConfigurableApplicationContext添加属性。 您可以使用key = value字符串调用它,如下所示:

TestPropertyValues.of("org=Spring", "name=Boot").applyTo(env);

4.24.4.3 OutputCapture

OutputCapture是一个JUnit规则,可用于捕获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"));
	}

}

4.24.4.4 TestRestTemplate

Spring Framework 5.0提供了一个新的WebTestClient,适用于WebFlux集成测试以及WebFlux和MVC端到端测试。 与TestRestTemplate不同,它为断言提供了Stream的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");
	}

}