測試工具
spring-boot 中包含了一些在測試應用程式時通常很有用的測試工具類。
ConfigDataApplicationContextInitializer
ConfigDataApplicationContextInitializer 是一個 ApplicationContextInitializer,你可以將其應用於測試以載入 Spring Boot application.properties 檔案。當你不需要 @SpringBootTest 提供的所有功能時,可以使用它,如下例所示:
-
Java
-
Kotlin
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 允許你快速地向 ConfigurableEnvironment 或 ConfigurableApplicationContext 新增屬性。你可以使用 key=value 字串呼叫它,如下所示:
-
Java
-
Kotlin
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.out 和 System.err 輸出。要使用它,新增 @ExtendWith(OutputCaptureExtension.class) 並將 CapturedOutput 作為引數注入到你的測試類建構函式或測試方法中,如下所示:
-
Java
-
Kotlin
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 模組提供。
你可以獲取一個普通的模板,或者一個傳送基本 HTTP 認證(帶使用者名稱和密碼)的模板。在任何一種情況下,該模板都是容錯的。這意味著它以對測試友好的方式執行,不會在 4xx 和 5xx 錯誤時丟擲異常。相反,可以透過返回的 ResponseEntity 及其狀態碼檢測到這些錯誤。
如果你需要用於斷言的流式 API,可以考慮使用 RestTestClient,它適用於模擬環境和端到端測試。
如果你正在使用 Spring WebFlux,可以考慮使用 WebTestClient,它提供了類似的 API,並適用於模擬環境、WebFlux 整合測試和端到端測試。
建議但不強制使用 Apache HTTP Client(5.1 或更高版本)。如果你的類路徑中包含它,TestRestTemplate 將透過適當配置客戶端來響應。如果你確實使用 Apache 的 HTTP 客戶端,它會被配置為忽略 cookie(因此模板是無狀態的)。
TestRestTemplate 可以直接在你的整合測試中例項化,如下例所示:
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.boot.resttestclient.TestRestTemplate;
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> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.resttestclient.TestRestTemplate
class MyTests {
private val template = TestRestTemplate()
@Test
fun testRequest() {
val headers = template.getForEntity("https://myhost.example.com/example", String::class.java)
assertThat(headers.headers.location).hasHost("other.example.com")
}
}
另外,如果你使用帶有 WebEnvironment.RANDOM_PORT 或 WebEnvironment.DEFINED_PORT 的 @SpringBootTest 註解,可以透過使用 @AutoConfigureTestRestTemplate 註解測試類來注入一個完全配置的 TestRestTemplate。如有必要,可以透過 RestTemplateBuilder bean 進行額外的自定義。
所有未指定主機和埠的 URL 都會自動連線到嵌入式伺服器,如下例所示:
-
Java
-
Kotlin
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.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureTestRestTemplate
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@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 java.time.Duration
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureTestRestTemplate
class MySpringBootTests(@Autowired val template: TestRestTemplate) {
@Test
fun testRequest() {
val headers = template.getForEntity("/example", String::class.java).headers
assertThat(headers.location).hasHost("other.example.com")
}
@TestConfiguration(proxyBeanMethods = false)
internal class RestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(): RestTemplateBuilder {
return RestTemplateBuilder().connectTimeout(Duration.ofSeconds(1))
.readTimeout(Duration.ofSeconds(1))
}
}
}