此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Boot 4.0.4!spring-doc.cadn.net.cn

测试工具

一些在测试应用程序时通常有用的测试实用工具类被打包为 spring-boot 的一部分。spring-doc.cadn.net.cn

ConfigDataApplicationContextInitializer

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

import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;

@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {

	// ...

}
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer
import org.springframework.test.context.ContextConfiguration

@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {

	// ...

}
单独使用 ConfigDataApplicationContextInitializer 并不提供对 @Value("${…​}") 注入的支持。 它的唯一工作是确保将 application.properties 文件加载到 Spring 的 Environment 中。 若要获得 @Value 支持,您需要额外配置一个 PropertySourcesPlaceholderConfigurer,或者使用 @SpringBootTest,后者会自动为您配置一个。

TestPropertyValues

TestPropertyValues 可让您快速向 ConfigurableEnvironmentConfigurableApplicationContext 添加属性。 您可以使用 key=value 字符串调用它,如下所示:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;

import static org.assertj.core.api.Assertions.assertThat;

class MyEnvironmentTests {

	@Test
	void testPropertySources() {
		MockEnvironment environment = new MockEnvironment();
		TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
		assertThat(environment.getProperty("name")).isEqualTo("Boot");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.test.util.TestPropertyValues
import org.springframework.mock.env.MockEnvironment

class MyEnvironmentTests {

	@Test
	fun testPropertySources() {
		val environment = MockEnvironment()
		TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment)
		assertThat(environment.getProperty("name")).isEqualTo("Boot")
	}

}

OutputCaptureExtension

OutputCaptureExtension 是一个 JUnit Extension,您可以使用它来捕获 System.outSystem.err 输出。 要使用它,请添加 @ExtendWith(OutputCaptureExtension.class) 并将 CapturedOutput 作为参数注入到您的测试类构造函数或测试方法中,如下所示:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {

	@Test
	void testName(CapturedOutput output) {
		System.out.println("Hello World!");
		assertThat(output).contains("World");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.system.CapturedOutput
import org.springframework.boot.test.system.OutputCaptureExtension

@ExtendWith(OutputCaptureExtension::class)
class MyOutputCaptureTests {

	@Test
	fun testName(output: CapturedOutput?) {
		println("Hello World!")
		assertThat(output).contains("World")
	}

}

TestRestTemplate

TestRestTemplate 是 Spring 的 RestTemplate 的便捷替代方法,适用于集成测试。 它由 spring-boot-resttestclient 模块提供。 还需要 spring-boot-restclient 的依赖。 添加此依赖时要小心,因为它会为 RestClient.Builder 启用自动配置。 如果您的主代码使用 RestClient.Builder,请声明 spring-boot-restclient 依赖,以确保它在应用程序的主类路径上,而不仅仅在测试类路径上。spring-doc.cadn.net.cn

您可以获取一个原始模板,或者一个发送基本HTTP身份验证(带有用户名和密码)的模板。 无论哪种情况,该模板都是容错的。 这意味着它在测试时会以不抛出4xx和5xx错误异常的方式运行。 相反,可以通过返回的 ResponseEntity 及其状态码来检测此类错误。spring-doc.cadn.net.cn

如果您需要用于断言的流畅API,请考虑使用 RestTestClient,它适用于 模拟环境端到端测试spring-doc.cadn.net.cn

如果您使用的是Spring WebFlux,请考虑提供类似API的WebTestClient,它适用于模拟环境WebFlux集成测试端到端测试spring-doc.cadn.net.cn

建议使用 Apache HTTP Client(5.1 或更高版本),但并非强制要求。 如果您的类路径中包含该库,TestRestTemplate 会通过适当配置客户端来响应。 如果您使用 Apache 的 HTTP 客户端,它会被配置为忽略 Cookie(从而使模板无状态)。spring-doc.cadn.net.cn

TestRestTemplate 可以直接在集成测试中实例化,如下例所示:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;

import org.springframework.boot.resttestclient.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

class MyTests {

	private final TestRestTemplate template = new TestRestTemplate();

	@Test
	void testRequest() {
		ResponseEntity<String> response = this.template.getForEntity("https://myhost.example.com/example",
				String.class);
		assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
		// Other assertions to verify the response
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.resttestclient.TestRestTemplate
import org.springframework.http.HttpStatus

class MyTests {

	private val template = TestRestTemplate()

	@Test
	fun testRequest() {
		val response = template.getForEntity("https://myhost.example.com/example", String::class.java)
		assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
		// Other assertions to verify the response
	}

}

或者,如果您使用带有 @SpringBootTest 注解的 WebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORT,可以通过将测试类标注为 @AutoConfigureTestRestTemplate 来注入一个完全配置好的 TestRestTemplate。 如有需要,还可以通过 RestTemplateBuilder bean 应用其他自定义设置。spring-doc.cadn.net.cn

任何未指定主机和端口的URL都会自动连接到嵌入式服务器,如以下示例所示:spring-doc.cadn.net.cn

import java.time.Duration;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.resttestclient.TestRestTemplate;
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureTestRestTemplate
class MySpringBootTests {

	@Autowired
	private TestRestTemplate template;

	@Test
	void testRequest() {
		ResponseEntity<String> response = this.template.getForEntity("/example", String.class);
		assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
		// Other assertions to verify the response
	}

	@TestConfiguration(proxyBeanMethods = false)
	static class RestTemplateBuilderConfiguration {

		@Bean
		RestTemplateBuilder restTemplateBuilder() {
			return new RestTemplateBuilder().connectTimeout(Duration.ofSeconds(1)).readTimeout(Duration.ofSeconds(1));
		}

	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
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.context.TestConfiguration
import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.boot.resttestclient.TestRestTemplate
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate
import org.springframework.context.annotation.Bean
import org.springframework.http.HttpStatus
import java.time.Duration

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureTestRestTemplate
class MySpringBootTests(@Autowired val template: TestRestTemplate) {

	@Test
	fun testRequest() {
		val response = template.getForEntity("/example", String::class.java)
		assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
		// Other assertions to verify the response
	}

	@TestConfiguration(proxyBeanMethods = false)
	internal class RestTemplateBuilderConfiguration {

		@Bean
		fun restTemplateBuilder(): RestTemplateBuilder {
			return RestTemplateBuilder().connectTimeout(Duration.ofSeconds(1))
				.readTimeout(Duration.ofSeconds(1))
		}

	}

}