測試 Spring Boot 應用

Spring Boot 應用是一個 Spring ApplicationContext,因此除了通常對普通 Spring 上下文進行的操作之外,無需進行任何特殊處理即可對其進行測試。

外部屬性、日誌記錄和 Spring Boot 的其他特性預設只有在使用 SpringApplication 建立上下文時才會被安裝。

Spring Boot 提供了 @SpringBootTest 註解,當你需要 Spring Boot 特性時,它可以作為標準的 spring-test @ContextConfiguration 註解的替代品。該註解透過 SpringApplication 建立測試中使用的 ApplicationContext 來工作。除了 @SpringBootTest 之外,還提供了許多其他註解用於測試應用中更具體的“切片”

如果你使用的是 JUnit 4,不要忘記也在你的測試中新增 @RunWith(SpringRunner.class) 註解,否則這些註解將被忽略。如果你使用的是 JUnit 5,則無需新增等效的 @ExtendWith(SpringExtension.class),因為 @SpringBootTest 和其他 @…​Test 註解已經使用它進行了註解。

預設情況下,@SpringBootTest 不會啟動伺服器。你可以使用 @SpringBootTestwebEnvironment 屬性來進一步細化測試的執行方式

  • MOCK(預設值):載入一個 web ApplicationContext 並提供一個模擬的 web 環境。使用此註解時不會啟動嵌入式伺服器。如果你的 classpath 中沒有可用的 web 環境,此模式會透明地回退到建立一個常規的非 web ApplicationContext。它可以與@AutoConfigureMockMvc@AutoConfigureWebTestClient 結合使用,用於對你的 web 應用進行基於模擬的測試。

  • RANDOM_PORT:載入一個 WebServerApplicationContext 並提供一個真實的 web 環境。嵌入式伺服器會啟動並監聽隨機埠。

  • DEFINED_PORT:載入一個 WebServerApplicationContext 並提供一個真實的 web 環境。嵌入式伺服器會啟動並監聽指定的埠(來自你的 application.properties)或預設埠 8080

  • NONE:使用 SpringApplication 載入一個 ApplicationContext,但不提供*任何* web 環境(模擬的或其他)。

如果你的測試帶有 @Transactional 註解,預設情況下它會在每個測試方法結束時回滾事務。然而,由於在 RANDOM_PORTDEFINED_PORT 模式下使用此配置會隱式提供一個真實的 Servlet 環境,HTTP 客戶端和伺服器會在不同的執行緒中執行,因此在不同的事務中執行。在這種情況下,在伺服器上啟動的任何事務都不會回滾。
如果你的應用為管理伺服器使用了不同的埠,則使用 webEnvironment = WebEnvironment.RANDOM_PORT@SpringBootTest 也會在另一個隨機埠上啟動管理伺服器。

檢測 Web 應用型別

如果存在 Spring MVC,則會配置一個常規的基於 MVC 的應用上下文。如果你只有 Spring WebFlux,我們將檢測到這一點並配置一個基於 WebFlux 的應用上下文。

如果兩者都存在,Spring MVC 優先。在這種情況下,如果你想測試響應式 web 應用,你必須設定 spring.main.web-application-type 屬性

  • Java

  • Kotlin

import org.springframework.boot.test.context.SpringBootTest;

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

	// ...

}
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {

	// ...

}

檢測測試配置

如果你熟悉 Spring Test Framework,你可能習慣於使用 @ContextConfiguration(classes=…​) 來指定要載入哪個 Spring @Configuration。或者,你可能經常在測試中使用巢狀的 @Configuration 類。

測試 Spring Boot 應用時,通常不需要這樣做。每當你沒有顯式定義主要配置時,Spring Boot 的 @*Test 註解會自動搜尋它。

搜尋演算法從包含測試的包向上查詢,直到找到帶有 @SpringBootApplication@SpringBootConfiguration 註解的類。只要你以合理的方式構建了程式碼,通常就能找到你的主配置。

如果你使用測試註解來測試應用中更具體的“切片”,則應避免在主方法的應用類上新增特定於某個區域的配置設定。

@SpringBootApplication 的底層元件掃描配置定義了用於確保“切片”按預期工作的排除過濾器。如果你在帶有 @SpringBootApplication 註解的類上使用了顯式的 @ComponentScan 指令,請注意這些過濾器將被停用。如果你正在使用“切片”,則應再次定義它們。

如果你想自定義主配置,可以使用巢狀的 @TestConfiguration 類。與用於替代你的應用主配置的巢狀 @Configuration 類不同,巢狀的 @TestConfiguration 類是在你的應用主配置之外使用的。

Spring 的測試框架會在測試之間快取應用上下文。因此,只要你的測試共享相同的配置(無論如何發現),載入上下文這個潛在耗時的過程只發生一次。

使用測試配置的主方法

通常,@SpringBootTest 發現的測試配置將是你的主 @SpringBootApplication。在大多數結構良好的應用中,這個配置類也將包含用於啟動應用的 main 方法。

例如,下面是一個典型 Spring Boot 應用中非常常見的程式碼模式

  • Java

  • Kotlin

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyApplication.class, args);
	}

}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.docs.using.structuringyourcode.locatingthemainclass.MyApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

在上面的例子中,main 方法除了委託給 SpringApplication.run(Class, String…​) 外不做任何事情。但是,也可以有一個更復雜的 main 方法,在呼叫 SpringApplication.run(Class, String…​) 之前應用自定義配置。

例如,這是一個改變 banner 模式並設定額外 profiles 的應用

  • Java

  • Kotlin

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(MyApplication.class);
		application.setBannerMode(Banner.Mode.OFF);
		application.setAdditionalProfiles("myprofile");
		application.run(args);
	}

}
import org.springframework.boot.Banner
import org.springframework.boot.runApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args) {
		setBannerMode(Banner.Mode.OFF)
		setAdditionalProfiles("myprofile")
	}
}

由於 main 方法中的自定義配置會影響最終的 ApplicationContext,你可能也想使用 main 方法來建立測試中使用的 ApplicationContext。預設情況下,@SpringBootTest 不會呼叫你的 main 方法,而是直接使用類本身來建立 ApplicationContext

如果你想改變這種行為,可以將 @SpringBootTestuseMainMethod 屬性更改為 SpringBootTest.UseMainMethod.ALWAYSSpringBootTest.UseMainMethod.WHEN_AVAILABLE。當設定為 ALWAYS 時,如果找不到 main 方法,測試將失敗。當設定為 WHEN_AVAILABLE 時,如果 main 方法可用,則會使用它,否則將使用標準的載入機制。

例如,以下測試將呼叫 MyApplicationmain 方法來建立 ApplicationContext。如果 main 方法設定了額外的 profiles,那麼當 ApplicationContext 啟動時,這些 profiles 將會啟用。

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod;

@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {

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

}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod

@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {

	@Test
	fun exampleTest() {
		// ...
	}

}

排除測試配置

如果你的應用使用元件掃描(例如,如果你使用了 @SpringBootApplication@ComponentScan),你可能會發現你僅為特定測試建立的頂級配置類意外地在所有地方都被掃描到。

如我們前面所見,可以在測試的內部類上使用 @TestConfiguration 來自定義主配置。@TestConfiguration 也可以用於頂級類。這樣做表明該類不應被掃描到。然後你可以在需要的地方顯式匯入該類,如下例所示

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {

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

}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import

@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {

	@Test
	fun exampleTest() {
		// ...
	}

}
如果你直接使用 @ComponentScan(即不透過 @SpringBootApplication),你需要將 TypeExcludeFilter 註冊到其中。詳情請參見 TypeExcludeFilter API 文件。
匯入的 @TestConfiguration 比內部類 @TestConfiguration 處理得更早,並且匯入的 @TestConfiguration 會在透過元件掃描找到的任何配置之前處理。一般來說,這種順序差異沒有明顯的影響,但如果你依賴於 bean 覆蓋,則需要注意這一點。

使用應用引數

如果你的應用需要引數,你可以讓 @SpringBootTest 使用 args 屬性注入它們。

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;

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

@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {

	@Test
	void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
		assertThat(args.getOptionNames()).containsOnly("app.test");
		assertThat(args.getOptionValues("app.test")).containsOnly("one");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {

	@Test
	fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
		assertThat(args.optionNames).containsOnly("app.test")
		assertThat(args.getOptionValues("app.test")).containsOnly("one")
	}

}

在模擬環境中測試

預設情況下,@SpringBootTest 不會啟動伺服器,而是設定一個模擬環境來測試 web 端點。

使用 Spring MVC,我們可以使用 MockMvc 查詢我們的 web 端點。有三種整合方式可用

以下示例展示了可用的整合方式

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

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.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

import static org.assertj.core.api.Assertions.assertThat;
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;

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

	@Test
	void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
		mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
	}

	// If AssertJ is on the classpath, you can use MockMvcTester
	@Test
	void testWithMockMvcTester(@Autowired MockMvcTester mvc) {
		assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("Hello World");
	}

	// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
	@Test
	void testWithWebTestClient(@Autowired WebTestClient webClient) {
		webClient
				.get().uri("/")
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello World");
	}

}
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.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.assertj.MockMvcTester

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

	@Test
	fun testWithMockMvc(@Autowired mvc: MockMvcTester) {
		assertThat(mvc.get().uri("/")).hasStatusOk()
				.hasBodyTextEqualTo("Hello World")
	}

	// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient

	@Test
	fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
		webClient
				.get().uri("/")
				.exchange()
				.expectStatus().isOk
				.expectBody<String>().isEqualTo("Hello World")
	}

}
如果你想只關注 web 層而不啟動完整的 ApplicationContext,可以考慮使用 @WebMvcTest

使用 Spring WebFlux 端點,你可以使用 WebTestClient,如下例所示

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

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.web.reactive.server.WebTestClient;

@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {

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

}
import org.junit.jupiter.api.Test
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.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {

	@Test
	fun exampleTest(@Autowired webClient: WebTestClient) {
		webClient
			.get().uri("/")
			.exchange()
			.expectStatus().isOk
			.expectBody<String>().isEqualTo("Hello World")
	}

}

在模擬環境中進行測試通常比在完整的 Servlet 容器中執行要快。然而,由於模擬發生在 Spring MVC 層,依賴於底層 Servlet 容器行為的程式碼無法直接使用 MockMvc 進行測試。

例如,Spring Boot 的錯誤處理基於 Servlet 容器提供的“錯誤頁”支援。這意味著,雖然你可以測試你的 MVC 層按預期丟擲和處理異常,但你不能直接測試是否渲染了特定的自定義錯誤頁。如果你需要測試這些底層問題,你可以啟動一個完全執行的伺服器,如下一節所述。

在執行的伺服器中測試

如果你需要啟動一個完全執行的伺服器,我們建議你使用隨機埠。如果你使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),每次測試執行時都會隨機選擇一個可用埠。

可以使用 @LocalServerPort 註解將實際使用的埠注入到你的測試中。為了方便起見,需要向已啟動伺服器進行 REST 呼叫的測試還可以自動注入一個 WebTestClient,它可以將相對連結解析到正在執行的伺服器,並提供一個專門用於驗證響應的 API,如下例所示

  • Java

  • Kotlin

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.test.web.reactive.server.WebTestClient;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {

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

}
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.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {

	@Test
	fun exampleTest(@Autowired webClient: WebTestClient) {
		webClient
			.get().uri("/")
			.exchange()
			.expectStatus().isOk
			.expectBody<String>().isEqualTo("Hello World")
	}

}
WebTestClient 也可以與模擬環境一起使用,透過使用 @AutoConfigureWebTestClient 註解測試類來消除對執行伺服器的需求。

此設定需要在 classpath 中包含 spring-webflux。如果你不能或不想新增 webflux,Spring Boot 還提供了 TestRestTemplate 工具

  • Java

  • Kotlin

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.web.client.TestRestTemplate;

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

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {

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

}
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.web.client.TestRestTemplate

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {

	@Test
	fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
		val body = restTemplate.getForObject("/", String::class.java)
		assertThat(body).isEqualTo("Hello World")
	}

}

自定義 WebTestClient

要自定義 WebTestClient bean,請配置一個 WebTestClientBuilderCustomizer bean。任何此類 bean 都會在建立 WebTestClient 時,使用用於構建 WebTestClientWebTestClient.Builder 被呼叫。

使用 JMX

由於測試上下文框架會快取上下文,JMX 預設是停用的,以防止相同的元件註冊到同一個域。如果此類測試需要訪問 MBeanServer,請考慮將其標記為 dirty。

  • Java

  • Kotlin

import javax.management.MBeanServer;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;

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

@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {

	@Autowired
	private MBeanServer mBeanServer;

	@Test
	void exampleTest() {
		assertThat(this.mBeanServer.getDomains()).contains("java.lang");
		// ...
	}

}
import javax.management.MBeanServer

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.test.annotation.DirtiesContext

@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {

	@Test
	fun exampleTest() {
		assertThat(mBeanServer.domains).contains("java.lang")
		// ...
	}

}

使用 Observations

如果你使用 @AutoConfigureObservability 註解 切片測試,它會自動配置一個 ObservationRegistry

使用 Metrics

無論你的 classpath 如何,除了記憶體支援的 meter registries 外,在使用 @SpringBootTest 時都不會自動配置。

如果你需要將 metrics 匯出到其他後端作為整合測試的一部分,請使用 @AutoConfigureObservability 註解進行標註。

如果你使用 @AutoConfigureObservability 註解 切片測試,它會自動配置一個記憶體型的 MeterRegistry。使用 @AutoConfigureObservability 註解時,不支援在切片測試中匯出資料。

使用 Tracing

無論你的 classpath 如何,在使用 @SpringBootTest 時,用於報告資料的 tracing 元件都不會自動配置。

如果你需要將這些元件作為整合測試的一部分,請使用 @AutoConfigureObservability 註解測試。

如果你建立了自己的報告元件(例如自定義的 SpanExporterbrave.handler.SpanHandler),並且不希望它們在測試中啟用,你可以使用 @ConditionalOnEnabledTracing 註解來停用它們。

如果你使用 @AutoConfigureObservability 註解 切片測試,它會自動配置一個 no-op Tracer。使用 @AutoConfigureObservability 註解時,不支援在切片測試中匯出資料。

Mocking 和 Spying Beans

執行測試時,有時需要模擬應用程式上下文中的某些元件。例如,你可能有一個遠端服務的門面 (facade),該服務在開發期間不可用。模擬 (Mocking) 也可以在你想要模擬在真實環境中難以觸發的故障時非常有用。

Spring Framework 包含 @MockitoBean 註解,可用於在你的 ApplicationContext 中為 bean 定義一個 Mockito mock。此外,@MockitoSpyBean 可用於定義一個 Mockito spy。在Spring Framework 文件中瞭解有關這些功能的更多資訊。

自動配置測試

Spring Boot 的自動配置系統對於應用程式來說很好,但有時對於測試來說可能有點過多。通常只加載測試應用程式“切片”所需的配置部分會很有幫助。例如,你可能想要測試 Spring MVC 控制器是否正確對映 URL,並且不希望在這些測試中涉及資料庫呼叫;或者你可能想要測試 JPA 實體,並且在這些測試執行時對 Web 層不感興趣。

spring-boot-test-autoconfigure 模組包含許多可用於自動配置此類“切片”的註解。它們都以類似的方式工作,提供一個載入 ApplicationContext@…​Test 註解,以及一個或多個可用於自定義自動配置設定的 @AutoConfigure…​ 註解。

每個切片都會限制元件掃描到適當的元件,並載入一組非常有限的自動配置類。如果你需要排除其中一個,大多數 @…​Test 註解都提供一個 excludeAutoConfiguration 屬性。或者,你可以使用 @ImportAutoConfiguration#exclude
不支援在同一個測試中使用多個 @…​Test 註解來包含多個“切片”。如果你需要多個“切片”,請選擇一個 @…​Test 註解,然後手動包含其他“切片”的 @AutoConfigure…​ 註解。
也可以將 @AutoConfigure…​ 註解與標準的 @SpringBootTest 註解一起使用。如果你對應用程式的“切片”不感興趣,但需要一些自動配置的測試 bean,可以使用這種組合。

自動配置的 JSON 測試

要測試物件的 JSON 序列化和反序列化是否按預期工作,你可以使用 @JsonTest 註解。@JsonTest 會自動配置可用的支援的 JSON 對映器,它可以是以下庫之一

@JsonTest 啟用的自動配置列表可以在附錄中找到

如果你需要配置自動配置的元素,可以使用 @AutoConfigureJsonTesters 註解。

Spring Boot 包含基於 AssertJ 的 helper 類,它們與 JSONAssert 和 JsonPath 庫配合使用,以檢查 JSON 是否按預期出現。JacksonTesterGsonTesterJsonbTesterBasicJsonTester 類可分別用於 Jackson、Gson、Jsonb 和字串。在使用 @JsonTest 時,測試類中的任何 helper 欄位都可以被 @Autowired 注入。下面的示例顯示了用於 Jackson 的測試類

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;

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

@JsonTest
class MyJsonTests {

	@Autowired
	private JacksonTester<VehicleDetails> json;

	@Test
	void serialize() 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
	void deserialize() 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");
	}

}
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.autoconfigure.json.JsonTest
import org.springframework.boot.test.json.JacksonTester

@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {

	@Test
	fun serialize() {
		val details = VehicleDetails("Honda", "Civic")
		// Assert against a `.json` file in the same package as the test
		assertThat(json.write(details)).isEqualToJson("expected.json")
		// Or use JSON path based assertions
		assertThat(json.write(details)).hasJsonPathStringValue("@.make")
		assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
	}

	@Test
	fun deserialize() {
		val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
		assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
		assertThat(json.parseObject(content).make).isEqualTo("Ford")
	}

}
JSON helper 類也可以直接用於標準單元測試。為此,如果你不使用 @JsonTest,請在你的 @BeforeEach 方法中呼叫 helper 的 initFields 方法。

如果你使用 Spring Boot 基於 AssertJ 的 helper 類來對給定 JSON 路徑下的數值進行斷言,根據型別不同,你可能無法使用 isEqualTo。相反,你可以使用 AssertJ 的 satisfies 來斷言該值符合給定條件。例如,下面的示例斷言實際數值是一個接近 0.15 且偏移量在 0.01 之內的浮點值。

  • Java

  • Kotlin

	@Test
	void someTest() throws Exception {
		SomeObject value = new SomeObject(0.152f);
		assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
			.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
	}
	@Test
	fun someTest() {
		val value = SomeObject(0.152f)
		assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
			.satisfies(ThrowingConsumer { number ->
				assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
			})
	}

自動配置的 Spring MVC 測試

要測試 Spring MVC 控制器是否按預期工作,使用 @WebMvcTest 註解。@WebMvcTest 會自動配置 Spring MVC 基礎設施,並將掃描的 bean 限制為 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterFilterHandlerInterceptorWebMvcConfigurerWebMvcRegistrationsHandlerMethodArgumentResolver。使用 @WebMvcTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@WebMvcTest 啟用的自動配置設定列表可以在附錄中找到
如果你需要註冊額外的元件,例如 Jackson Module,可以透過在測試類上使用 @Import 來匯入附加的配置類。

通常,@WebMvcTest 僅限於單個控制器,並與 @MockitoBean 結合使用,為所需的協作物件提供 mock 實現。

@WebMvcTest 也會自動配置 MockMvc。Mock MVC 提供了一種強大的方法,無需啟動完整的 HTTP 伺服器即可快速測試 MVC 控制器。如果 AssertJ 可用,MockMvcTester 提供的 AssertJ 支援也會自動配置。

你也可以透過使用 @AutoConfigureMockMvc 註解,在非 @WebMvcTest 測試(例如 @SpringBootTest)中自動配置 MockMvcMockMvcTester。下面的示例使用了 MockMvcTester
  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

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.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@WebMvcTest(UserVehicleController.class)
class MyControllerTests {

	@Autowired
	private MockMvcTester mvc;

	@MockitoBean
	private UserVehicleService userVehicleService;

	@Test
	void testExample() {
		given(this.userVehicleService.getVehicleDetails("sboot"))
			.willReturn(new VehicleDetails("Honda", "Civic"));
		assertThat(this.mvc.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
			.hasStatusOk()
			.hasBodyTextEqualTo("Honda Civic");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
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.bean.override.mockito.MockitoBean
import org.springframework.test.web.servlet.assertj.MockMvcTester

@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvcTester) {

	@MockitoBean
	lateinit var userVehicleService: UserVehicleService

	@Test
	fun testExample() {
		given(userVehicleService.getVehicleDetails("sboot"))
				.willReturn(VehicleDetails("Honda", "Civic"))
		assertThat(mvc.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
				.hasStatusOk().hasBodyTextEqualTo("Honda Civic")
	}

}
如果你需要配置自動配置的元素(例如,何時應用 servlet filter),可以使用 @AutoConfigureMockMvc 註解中的屬性。

如果你使用 HtmlUnit 和 Selenium,自動配置也會提供一個 HtmlUnit WebClient bean 和/或一個 Selenium WebDriver bean。下面的示例使用了 HtmlUnit

  • Java

  • Kotlin

import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {

	@Autowired
	private WebClient webClient;

	@MockitoBean
	private UserVehicleService userVehicleService;

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

}
import org.assertj.core.api.Assertions.assertThat
import org.htmlunit.WebClient
import org.htmlunit.html.HtmlPage
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.context.bean.override.mockito.MockitoBean

@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {

	@MockitoBean
	lateinit var userVehicleService: UserVehicleService

	@Test
	fun testExample() {
		given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
		val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
		assertThat(page.body.textContent).isEqualTo("Honda Civic")
	}

}
預設情況下,Spring Boot 將 WebDriver bean 放入一個特殊的“scope”中,以確保 driver 在每個測試後退出並注入新的例項。如果你不想要此行為,可以將 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) 新增到你的 WebDriver @Bean 定義中。
Spring Boot 建立的 webDriver scope 將替換任何使用者定義的同名 scope。如果你定義了自己的 webDriver scope,你可能會發現當使用 @WebMvcTest 時它會停止工作。

如果你的 classpath 中有 Spring Security,@WebMvcTest 也會掃描 WebSecurityConfigurer bean。你可以使用 Spring Security 的測試支援,而不是為此類測試完全停用安全性。有關如何使用 Spring Security 的 MockMvc 支援的更多詳細資訊,可以在此使用 Spring Security 進行測試 “How-to Guides” 部分中找到。

有時編寫 Spring MVC 測試還不夠;Spring Boot 可以幫助你執行使用實際伺服器進行完整的端到端測試

自動配置的 Spring WebFlux 測試

要測試 Spring WebFlux 控制器是否按預期工作,你可以使用 @WebFluxTest 註解。@WebFluxTest 會自動配置 Spring WebFlux 基礎設施,並將掃描的 bean 限制為 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFluxConfigurer。使用 @WebFluxTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@WebFluxTest 啟用的自動配置列表可以在附錄中找到
如果你需要註冊額外的元件,例如 Jackson Module,可以透過在測試類上使用 @Import 匯入附加的配置類。

通常,@WebFluxTest 僅限於單個控制器,並與 @MockitoBean 註解結合使用,為所需的協作物件提供 mock 實現。

@WebFluxTest 也會自動配置 WebTestClient,它提供了一種強大的方法,無需啟動完整的 HTTP 伺服器即可快速測試 WebFlux 控制器。

你也可以透過使用 @AutoConfigureWebTestClient 註解,在非 @WebFluxTest 測試(例如 @SpringBootTest)中自動配置 WebTestClient。下面的示例顯示了一個同時使用了 @WebFluxTestWebTestClient 的類
  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

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.bean.override.mockito.MockitoBean;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.mockito.BDDMockito.given;

@WebFluxTest(UserVehicleController.class)
class MyControllerTests {

	@Autowired
	private WebTestClient webClient;

	@MockitoBean
	private UserVehicleService userVehicleService;

	@Test
	void testExample() {
		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");
	}

}
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
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.bean.override.mockito.MockitoBean
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {

	@MockitoBean
	lateinit var userVehicleService: UserVehicleService

	@Test
	fun testExample() {
		given(userVehicleService.getVehicleDetails("sboot"))
			.willReturn(VehicleDetails("Honda", "Civic"))
		webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
			.expectStatus().isOk
			.expectBody<String>().isEqualTo("Honda Civic")
	}

}
此設定僅受 WebFlux 應用程式支援,因為目前在模擬的 Web 應用程式中使用 WebTestClient 只適用於 WebFlux。
@WebFluxTest 無法檢測透過函式式 Web 框架註冊的路由。要測試上下文中的 RouterFunction bean,請考慮透過使用 @Import 或使用 @SpringBootTest 來匯入你自己的 RouterFunction
@WebFluxTest 無法檢測註冊為型別為 SecurityWebFilterChain@Bean 的自定義安全配置。要將其包含在測試中,你需要透過使用 @Import 或使用 @SpringBootTest 匯入註冊該 bean 的配置。
有時編寫 Spring WebFlux 測試還不夠;Spring Boot 可以幫助你執行使用實際伺服器進行完整的端到端測試

自動配置的 Spring GraphQL 測試

Spring GraphQL 提供了一個專用的測試支援模組;你需要將其新增到你的專案中

Maven
<dependencies>
	<dependency>
		<groupId>org.springframework.graphql</groupId>
		<artifactId>spring-graphql-test</artifactId>
		<scope>test</scope>
	</dependency>
	<!-- Unless already present in the compile scope -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-webflux</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>
Gradle
dependencies {
	testImplementation("org.springframework.graphql:spring-graphql-test")
	// Unless already present in the implementation configuration
	testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}

這個測試模組提供了 GraphQlTester。這個 tester 在測試中被大量使用,所以請務必熟悉它的用法。有不同變種的 GraphQlTester,Spring Boot 會根據測試型別自動配置它們

Spring Boot 透過 @GraphQlTest 註解幫助你測試你的 Spring GraphQL Controller@GraphQlTest 會自動配置 Spring GraphQL 基礎設施,不涉及任何傳輸或伺服器。這將掃描的 bean 限制為 @ControllerRuntimeWiringConfigurerJsonComponentConverterGenericConverterDataFetcherExceptionResolverInstrumentationGraphQlSourceBuilderCustomizer。使用 @GraphQlTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@GraphQlTest 啟用的自動配置列表可以在附錄中找到

通常,@GraphQlTest 僅限於一組控制器,並與 @MockitoBean 註解結合使用,為所需的協作物件提供 mock 實現。

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;

@GraphQlTest(GreetingController.class)
class GreetingControllerTests {

	@Autowired
	private GraphQlTester graphQlTester;

	@Test
	void shouldGreetWithSpecificName() {
		this.graphQlTester.document("{ greeting(name: \"Alice\") } ")
			.execute()
			.path("greeting")
			.entity(String.class)
			.isEqualTo("Hello, Alice!");
	}

	@Test
	void shouldGreetWithDefaultName() {
		this.graphQlTester.document("{ greeting } ")
			.execute()
			.path("greeting")
			.entity(String.class)
			.isEqualTo("Hello, Spring!");
	}

}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest
import org.springframework.graphql.test.tester.GraphQlTester

@GraphQlTest(GreetingController::class)
internal class GreetingControllerTests {

	@Autowired
	lateinit var graphQlTester: GraphQlTester

	@Test
	fun shouldGreetWithSpecificName() {
		graphQlTester.document("{ greeting(name: \"Alice\") } ").execute().path("greeting").entity(String::class.java)
				.isEqualTo("Hello, Alice!")
	}

	@Test
	fun shouldGreetWithDefaultName() {
		graphQlTester.document("{ greeting } ").execute().path("greeting").entity(String::class.java)
				.isEqualTo("Hello, Spring!")
	}

}

@SpringBootTest 測試是完整的整合測試,涉及整個應用程式。使用隨機埠或指定埠時,會配置 live server,並自動貢獻一個 HttpGraphQlTester bean,這樣你就可以用它來測試你的伺服器。配置 MOCK 環境時,你也可以透過使用 @AutoConfigureHttpGraphQlTester 註解測試類來請求一個 HttpGraphQlTester bean

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.HttpGraphQlTester;

@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {

	@Test
	void shouldGreetWithSpecificName(@Autowired HttpGraphQlTester graphQlTester) {
		HttpGraphQlTester authenticatedTester = graphQlTester.mutate()
			.webTestClient((client) -> client.defaultHeaders((headers) -> headers.setBasicAuth("admin", "ilovespring")))
			.build();
		authenticatedTester.document("{ greeting(name: \"Alice\") } ")
			.execute()
			.path("greeting")
			.entity(String.class)
			.isEqualTo("Hello, Alice!");
	}

}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.graphql.test.tester.HttpGraphQlTester
import org.springframework.http.HttpHeaders
import org.springframework.test.web.reactive.server.WebTestClient

@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {

	@Test
	fun shouldGreetWithSpecificName(@Autowired graphQlTester: HttpGraphQlTester) {
		val authenticatedTester = graphQlTester.mutate()
			.webTestClient { client: WebTestClient.Builder ->
				client.defaultHeaders { headers: HttpHeaders ->
					headers.setBasicAuth("admin", "ilovespring")
				}
			}.build()
		authenticatedTester.document("{ greeting(name: \"Alice\") } ").execute()
			.path("greeting").entity(String::class.java).isEqualTo("Hello, Alice!")
	}
}

自動配置的 Data Cassandra 測試

你可以使用 @DataCassandraTest 來測試 Cassandra 應用程式。預設情況下,它配置一個 CassandraTemplate,掃描 @Table 類,並配置 Spring Data Cassandra repositories。使用 @DataCassandraTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(有關在 Spring Boot 中使用 Cassandra 的更多資訊,請參閱Cassandra。)

@DataCassandraTest 啟用的自動配置設定列表可以在附錄中找到

下面的示例顯示了在 Spring Boot 中使用 Cassandra 測試的典型設定

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;

@DataCassandraTest
class MyDataCassandraTests {

	@Autowired
	private SomeRepository repository;

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest

@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)

自動配置的 Data Couchbase 測試

你可以使用 @DataCouchbaseTest 來測試 Couchbase 應用程式。預設情況下,它配置一個 CouchbaseTemplate 或一個 ReactiveCouchbaseTemplate,掃描 @Document 類,並配置 Spring Data Couchbase repositories。使用 @DataCouchbaseTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(有關在 Spring Boot 中使用 Couchbase 的更多資訊,請參閱Couchbase。)

@DataCouchbaseTest 啟用的自動配置設定列表可以在附錄中找到

下面的示例顯示了在 Spring Boot 中使用 Couchbase 測試的典型設定

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;

@DataCouchbaseTest
class MyDataCouchbaseTests {

	@Autowired
	private SomeRepository repository;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest

@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {

	// ...

}

自動配置的 Data Elasticsearch 測試

你可以使用 @DataElasticsearchTest 來測試 Elasticsearch 應用程式。預設情況下,它配置一個 ElasticsearchTemplate,掃描 @Document 類,並配置 Spring Data Elasticsearch repositories。使用 @DataElasticsearchTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(有關在 Spring Boot 中使用 Elasticsearch 的更多資訊,請參閱Elasticsearch。)

@DataElasticsearchTest 啟用的自動配置設定列表可以在附錄中找到

下面的示例顯示了在 Spring Boot 中使用 Elasticsearch 測試的典型設定

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;

@DataElasticsearchTest
class MyDataElasticsearchTests {

	@Autowired
	private SomeRepository repository;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest

@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {

	// ...

}

自動配置的 Data JPA 測試

你可以使用 @DataJpaTest 註解來測試 JPA 應用程式。預設情況下,它掃描 @Entity 類並配置 Spring Data JPA repositories。如果 classpath 中有嵌入式資料庫,它也會配置一個。透過將 spring.jpa.show-sql 屬性設定為 true,SQL 查詢預設會被記錄。可以使用註解的 showSql 屬性停用此功能。

使用 @DataJpaTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@DataJpaTest 啟用的自動配置設定列表可以在附錄中找到

預設情況下,Data JPA 測試是事務性的,並在每個測試結束時回滾。更多詳細資訊,請參閱 Spring Framework 參考文件中的相關章節。如果這不是你想要的,可以按如下方式為單個測試或整個類停用事務管理

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {

	// ...

}
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {

	// ...

}

Data JPA 測試也可能注入一個 TestEntityManager bean,它提供了標準 JPA EntityManager 的替代方案,專為測試設計。

TestEntityManager 也可以透過新增 @AutoConfigureTestEntityManager,自動配置到你的任何基於 Spring 的測試類中。進行此操作時,請確保你的測試執行在事務中,例如透過在測試類或方法上新增 @Transactional

如果你需要 JdbcTemplate,它也是可用的。下面的示例展示了正在使用的 @DataJpaTest 註解

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

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

@DataJpaTest
class MyRepositoryTests {

	@Autowired
	private TestEntityManager entityManager;

	@Autowired
	private UserRepository repository;

	@Test
	void testExample() {
		this.entityManager.persist(new User("sboot", "1234"));
		User user = this.repository.findByUsername("sboot");
		assertThat(user.getUsername()).isEqualTo("sboot");
		assertThat(user.getEmployeeNumber()).isEqualTo("1234");
	}

}
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.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager

@DataJpaTest
class MyRepositoryTests(@Autowired val entityManager: TestEntityManager, @Autowired val repository: UserRepository) {

	@Test
	fun testExample() {
		entityManager.persist(User("sboot", "1234"))
		val user = repository.findByUsername("sboot")
		assertThat(user?.username).isEqualTo("sboot")
		assertThat(user?.employeeNumber).isEqualTo("1234")
	}

}

記憶體中的嵌入式資料庫通常很適合用於測試,因為它們速度快且不需要任何安裝。然而,如果您希望針對真實的資料庫執行測試,可以使用 @AutoConfigureTestDatabase 註解,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {

	// ...

}
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MyRepositoryTests {

	// ...

}

自動配置的 JDBC 測試

@JdbcTest 類似於 @DataJpaTest,但適用於僅需要 DataSource 而不使用 Spring Data JDBC 的測試。預設情況下,它配置一個記憶體嵌入式資料庫和一個 JdbcTemplate。當使用 @JdbcTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@JdbcTest 啟用的自動配置列表在附錄中可以找到

預設情況下,JDBC 測試是事務性的,並在每個測試結束時回滾。更多詳情請參見 Spring Framework 參考文件中的相關部分。如果這不是您想要的行為,可以為某個測試或整個類停用事務管理,如下所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {

}
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests

如果您希望測試針對真實的資料庫執行,可以使用 @AutoConfigureTestDatabase 註解,用法與 @DataJpaTest 相同。(參見自動配置的 Data JPA 測試。)

自動配置的 Data JDBC 測試

@DataJdbcTest 類似於 @JdbcTest,但適用於使用 Spring Data JDBC 倉庫的測試。預設情況下,它配置一個記憶體嵌入式資料庫、一個 JdbcTemplate 和 Spring Data JDBC 倉庫。當使用 @DataJdbcTest 註解時,只會掃描 AbstractJdbcConfiguration 的子類,常規的 @Component@ConfigurationProperties bean 不會被掃描。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@DataJdbcTest 啟用的自動配置列表在附錄中可以找到

預設情況下,Data JDBC 測試是事務性的,並在每個測試結束時回滾。更多詳情請參見 Spring Framework 參考文件中的相關部分。如果這不是您想要的行為,可以為某個測試或整個測試類停用事務管理,如JDBC 示例中所示

如果您希望測試針對真實的資料庫執行,可以使用 @AutoConfigureTestDatabase 註解,用法與 @DataJpaTest 相同。(參見自動配置的 Data JPA 測試。)

自動配置的 Data R2DBC 測試

@DataR2dbcTest 類似於 @DataJdbcTest,但適用於使用 Spring Data R2DBC 倉庫的測試。預設情況下,它配置一個記憶體嵌入式資料庫、一個 R2dbcEntityTemplate 和 Spring Data R2DBC 倉庫。當使用 @DataR2dbcTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@DataR2dbcTest 啟用的自動配置設定列表在附錄中可以找到

預設情況下,Data R2DBC 測試不是事務性的。

如果您希望測試針對真實的資料庫執行,可以使用 @AutoConfigureTestDatabase 註解,用法與 @DataJpaTest 相同。(參見自動配置的 Data JPA 測試。)

自動配置的 jOOQ 測試

您可以像使用 @JdbcTest 一樣使用 @JooqTest,但它用於 jOOQ 相關的測試。由於 jOOQ 嚴重依賴於與資料庫模式相對應的基於 Java 的模式,因此會使用現有的 DataSource。如果您想用記憶體資料庫替換它,可以使用 @AutoConfigureTestDatabase 來覆蓋這些設定。(更多關於在 Spring Boot 中使用 jOOQ 的資訊,請參見使用 jOOQ。)當使用 @JooqTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@JooqTest 啟用的自動配置列表在附錄中可以找到

@JooqTest 配置了一個 DSLContext。以下示例展示了 @JooqTest 註解的使用方法

  • Java

  • Kotlin

import org.jooq.DSLContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;

@JooqTest
class MyJooqTests {

	@Autowired
	private DSLContext dslContext;

	// ...

}
import org.jooq.DSLContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jooq.JooqTest

@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {

	// ...

}

預設情況下,jOOQ 測試是事務性的,並在每個測試結束時回滾。如果這不是您想要的行為,可以為某個測試或整個測試類停用事務管理,如JDBC 示例中所示

自動配置的 Data MongoDB 測試

您可以使用 @DataMongoTest 來測試 MongoDB 應用。預設情況下,它配置一個 MongoTemplate,掃描 @Document 類,並配置 Spring Data MongoDB 倉庫。當使用 @DataMongoTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(更多關於在 Spring Boot 中使用 MongoDB 的資訊,請參見MongoDB。)

@DataMongoTest 啟用的自動配置設定列表在附錄中可以找到

以下類展示了 @DataMongoTest 註解的使用方法

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;

@DataMongoTest
class MyDataMongoDbTests {

	@Autowired
	private MongoTemplate mongoTemplate;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest
import org.springframework.data.mongodb.core.MongoTemplate

@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {

	// ...

}

自動配置的 Data Neo4j 測試

您可以使用 @DataNeo4jTest 來測試 Neo4j 應用。預設情況下,它掃描 @Node 類,並配置 Spring Data Neo4j 倉庫。當使用 @DataNeo4jTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(更多關於在 Spring Boot 中使用 Neo4j 的資訊,請參見Neo4j。)

@DataNeo4jTest 啟用的自動配置設定列表在附錄中可以找到

以下示例展示了在 Spring Boot 中使用 Neo4j 測試的典型設定

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;

@DataNeo4jTest
class MyDataNeo4jTests {

	@Autowired
	private SomeRepository repository;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest

@DataNeo4jTest
class MyDataNeo4jTests(@Autowired val repository: SomeRepository) {

	// ...

}

預設情況下,Data Neo4j 測試是事務性的,並在每個測試結束時回滾。更多詳情請參見 Spring Framework 參考文件中的相關部分。如果這不是您想要的行為,可以為某個測試或整個類停用事務管理,如下所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {

}
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
響應式訪問不支援事務性測試。如果您使用這種風格,則必須按照上述說明配置 @DataNeo4jTest 測試。

自動配置的 Data Redis 測試

您可以使用 @DataRedisTest 來測試 Redis 應用。預設情況下,它掃描 @RedisHash 類並配置 Spring Data Redis 倉庫。當使用 @DataRedisTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(更多關於在 Spring Boot 中使用 Redis 的資訊,請參見Redis。)

@DataRedisTest 啟用的自動配置設定列表在附錄中可以找到

以下示例展示了 @DataRedisTest 註解的使用方法

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;

@DataRedisTest
class MyDataRedisTests {

	@Autowired
	private SomeRepository repository;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest

@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {

	// ...

}

自動配置的 Data LDAP 測試

您可以使用 @DataLdapTest 來測試 LDAP 應用。預設情況下,它配置一個記憶體嵌入式 LDAP(如果可用)、配置一個 LdapTemplate,掃描 @Entry 類,並配置 Spring Data LDAP 倉庫。當使用 @DataLdapTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。(更多關於在 Spring Boot 中使用 LDAP 的資訊,請參見LDAP。)

@DataLdapTest 啟用的自動配置設定列表在附錄中可以找到

以下示例展示了 @DataLdapTest 註解的使用方法

  • Java

  • Kotlin

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;

@DataLdapTest
class MyDataLdapTests {

	@Autowired
	private LdapTemplate ldapTemplate;

	// ...

}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest
import org.springframework.ldap.core.LdapTemplate

@DataLdapTest
class MyDataLdapTests(@Autowired val ldapTemplate: LdapTemplate) {

	// ...

}

記憶體嵌入式 LDAP 通常很適合用於測試,因為它速度快且不需要開發人員進行任何安裝。然而,如果您希望針對真實的 LDAP 伺服器執行測試,則應排除嵌入式 LDAP 的自動配置,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;

@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {

	// ...

}
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest

@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {

	// ...

}

自動配置的 REST 客戶端

您可以使用 @RestClientTest 註解來測試 REST 客戶端。預設情況下,它自動配置 Jackson、GSON 和 Jsonb 支援,配置一個 RestTemplateBuilder 和一個 RestClient.Builder,並新增對 MockRestServiceServer 的支援。當使用 @RestClientTest 註解時,不會掃描常規的 @Component@ConfigurationProperties bean。@EnableConfigurationProperties 可用於包含 @ConfigurationProperties bean。

@RestClientTest 啟用的自動配置設定列表在附錄中可以找到

您要測試的特定 bean 應透過使用 @RestClientTestvaluecomponents 屬性來指定。

當在被測試的 bean 中使用 RestTemplateBuilder 並且在構建 RestTemplate 時呼叫了 RestTemplateBuilder.rootUri(String rootUri),則根 URI 應從 MockRestServiceServer 期望中省略,如下例所示

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

@RestClientTest(org.springframework.boot.docs.testing.springbootapplications.autoconfiguredrestclient.RemoteVehicleDetailsService.class)
class MyRestTemplateServiceTests {

	@Autowired
	private RemoteVehicleDetailsService service;

	@Autowired
	private MockRestServiceServer server;

	@Test
	void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
		this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
		String greeting = this.service.callRestService();
		assertThat(greeting).isEqualTo("hello");
	}

}
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.autoconfigure.web.client.RestClientTest
import org.springframework.http.MediaType
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators

@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestTemplateServiceTests(
	@Autowired val service: RemoteVehicleDetailsService,
	@Autowired val server: MockRestServiceServer) {

	@Test
	fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
		server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
			.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
		val greeting = service.callRestService()
		assertThat(greeting).isEqualTo("hello")
	}

}

當在被測試的 bean 中使用 RestClient.Builder,或者使用 RestTemplateBuilder 但沒有呼叫 rootUri(String rootURI) 時,必須在 MockRestServiceServer 期望中使用完整的 URI,如下例所示

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientServiceTests {

	@Autowired
	private RemoteVehicleDetailsService service;

	@Autowired
	private MockRestServiceServer server;

	@Test
	void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
		this.server.expect(requestTo("https://example.com/greet/details"))
			.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
		String greeting = this.service.callRestService();
		assertThat(greeting).isEqualTo("hello");
	}

}
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.autoconfigure.web.client.RestClientTest
import org.springframework.http.MediaType
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators

@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestClientServiceTests(
	@Autowired val service: RemoteVehicleDetailsService,
	@Autowired val server: MockRestServiceServer) {

	@Test
	fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
		server.expect(MockRestRequestMatchers.requestTo("https://example.com/greet/details"))
			.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
		val greeting = service.callRestService()
		assertThat(greeting).isEqualTo("hello")
	}

}

自動配置的 Spring REST Docs 測試

您可以使用 @AutoConfigureRestDocs 註解在您的測試中使用 Spring REST Docs,支援 Mock MVC、REST Assured 或 WebTestClient。它消除了 Spring REST Docs 中對 JUnit 擴充套件的需求。

@AutoConfigureRestDocs 可用於覆蓋預設的輸出目錄(如果使用 Maven 則為 target/generated-snippets,如果使用 Gradle 則為 build/generated-snippets)。它還可以用於配置出現在任何文件化 URI 中的主機、方案和埠。

使用 Mock MVC 的自動配置 Spring REST Docs 測試

@AutoConfigureRestDocs 會定製 MockMvc bean,以便在測試基於 Servlet 的 Web 應用時使用 Spring REST Docs。您可以使用 @Autowired 注入它,並在測試中使用它,就像您通常使用 Mock MVC 和 Spring REST Docs 時一樣,如下例所示

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

	@Autowired
	private MockMvcTester mvc;

	@Test
	void listUsers() {
		assertThat(this.mvc.get().uri("/users").accept(MediaType.TEXT_PLAIN)).hasStatusOk()
			.apply(document("list-users"));
	}

}

如果您更喜歡使用 AssertJ 整合,MockMvcTester 也可用,如下例所示

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

	@Autowired
	private MockMvcTester mvc;

	@Test
	void listUsers() {
		assertThat(this.mvc.get().uri("/users").accept(MediaType.TEXT_PLAIN)).hasStatusOk()
			.apply(document("list-users"));
	}

}

兩者在後臺重用相同的 MockMvc 例項,因此對其進行的任何配置都適用於兩者。

如果您需要比 @AutoConfigureRestDocs 屬性提供的對 Spring REST Docs 配置更多的控制,可以使用 RestDocsMockMvcConfigurationCustomizer bean,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {

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

}
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsMockMvcConfigurationCustomizer {

	override fun customize(configurer: MockMvcRestDocumentationConfigurer) {
		configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
	}

}

如果您想利用 Spring REST Docs 對引數化輸出目錄的支援,可以建立一個 RestDocumentationResultHandler bean。自動配置會使用此結果處理器呼叫 alwaysDo,從而導致每次 MockMvc 呼叫都自動生成預設片段。以下示例展示瞭如何定義 RestDocumentationResultHandler

  • Java

  • Kotlin

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;

@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {

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

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler

@TestConfiguration(proxyBeanMethods = false)
class MyResultHandlerConfiguration {

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

}

使用 WebTestClient 的自動配置 Spring REST Docs 測試

@AutoConfigureRestDocs 在測試響應式 Web 應用時也可與 WebTestClient 一起使用。您可以使用 @Autowired 注入它,並在您的測試中使用它,就像您通常使用 @WebFluxTest 和 Spring REST Docs 時一樣,如下例所示

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {

	@Autowired
	private WebTestClient webTestClient;

	@Test
	void listUsers() {
		this.webTestClient
			.get().uri("/")
		.exchange()
		.expectStatus()
			.isOk()
		.expectBody()
			.consumeWith(document("list-users"));
	}

}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests(@Autowired val webTestClient: WebTestClient) {

	@Test
	fun listUsers() {
		webTestClient
			.get().uri("/")
			.exchange()
			.expectStatus()
			.isOk
			.expectBody()
			.consumeWith(WebTestClientRestDocumentation.document("list-users"))
	}

}

如果您需要比 @AutoConfigureRestDocs 屬性提供的對 Spring REST Docs 配置更多的控制,可以使用 RestDocsWebTestClientConfigurationCustomizer bean,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {

	@Override
	public void customize(WebTestClientRestDocumentationConfigurer configurer) {
		configurer.snippets().withEncoding("UTF-8");
	}

}
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsWebTestClientConfigurationCustomizer {

	override fun customize(configurer: WebTestClientRestDocumentationConfigurer) {
		configurer.snippets().withEncoding("UTF-8")
	}

}

如果您想利用 Spring REST Docs 對引數化輸出目錄的支援,可以使用 WebTestClientBuilderCustomizer 為每個實體交換結果配置一個消費者。以下示例展示瞭如何定義這樣的 WebTestClientBuilderCustomizer

  • Java

  • Kotlin

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer;
import org.springframework.context.annotation.Bean;

import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {

	@Bean
	public WebTestClientBuilderCustomizer restDocumentation() {
		return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
	}

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient

@TestConfiguration(proxyBeanMethods = false)
class MyWebTestClientBuilderCustomizerConfiguration {

	@Bean
	fun restDocumentation(): WebTestClientBuilderCustomizer {
		return WebTestClientBuilderCustomizer { builder: WebTestClient.Builder ->
			builder.entityExchangeResultConsumer(
				WebTestClientRestDocumentation.document("{method-name}")
			)
		}
	}

}

使用 REST Assured 的自動配置 Spring REST Docs 測試

@AutoConfigureRestDocs 提供一個預配置為使用 Spring REST Docs 的 RequestSpecification bean 供您在測試中使用。您可以使用 @Autowired 注入它,並在測試中使用它,就像您通常使用 REST Assured 和 Spring REST Docs 時一樣,如下例所示

  • Java

  • Kotlin

import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;

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.test.web.server.LocalServerPort;

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

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

	@Test
	void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
		given(documentationSpec)
			.filter(document("list-users"))
		.when()
			.port(port)
			.get("/")
		.then().assertThat()
			.statusCode(is(200));
	}

}
import io.restassured.RestAssured
import io.restassured.specification.RequestSpecification
import org.hamcrest.Matchers
import org.junit.jupiter.api.Test
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.test.web.server.LocalServerPort
import org.springframework.restdocs.restassured.RestAssuredRestDocumentation

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

	@Test
	fun listUsers(@Autowired documentationSpec: RequestSpecification?, @LocalServerPort port: Int) {
		RestAssured.given(documentationSpec)
			.filter(RestAssuredRestDocumentation.document("list-users"))
			.`when`()
			.port(port)["/"]
			.then().assertThat()
			.statusCode(Matchers.`is`(200))
	}

}

如果您需要比 @AutoConfigureRestDocs 屬性提供的對 Spring REST Docs 配置更多的控制,可以使用 RestDocsRestAssuredConfigurationCustomizer bean,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

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

}
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {

	override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
		configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
	}

}

自動配置的 Spring Web Services 測試

自動配置的 Spring Web Services 客戶端測試

您可以使用 @WebServiceClientTest 來測試使用 Spring Web Services 專案呼叫 Web 服務的應用。預設情況下,它配置一個 MockWebServiceServer bean 並自動定製您的 WebServiceTemplateBuilder。(更多關於在 Spring Boot 中使用 Web Services 的資訊,請參見Web Services。)

@WebServiceClientTest 啟用的自動配置設定列表在附錄中可以找到

以下示例展示了 @WebServiceClientTest 註解的使用方法

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;

@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {

	@Autowired
	private MockWebServiceServer server;

	@Autowired
	private SomeWebService someWebService;

	@Test
	void mockServerCall() {
		this.server
			.expect(payload(new StringSource("<request/>")))
			.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
		assertThat(this.someWebService.test())
			.extracting(Response::getStatus)
			.isEqualTo(200);
	}

}
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.autoconfigure.webservices.client.WebServiceClientTest
import org.springframework.ws.test.client.MockWebServiceServer
import org.springframework.ws.test.client.RequestMatchers
import org.springframework.ws.test.client.ResponseCreators
import org.springframework.xml.transform.StringSource

@WebServiceClientTest(SomeWebService::class)
class MyWebServiceClientTests(@Autowired val server: MockWebServiceServer, @Autowired val someWebService: SomeWebService) {

	@Test
	fun mockServerCall() {
		server
			.expect(RequestMatchers.payload(StringSource("<request/>")))
			.andRespond(ResponseCreators.withPayload(StringSource("<response><status>200</status></response>")))
		assertThat(this.someWebService.test()).extracting(Response::status).isEqualTo(200)
	}

}

自動配置的 Spring Web Services 伺服器測試

您可以使用 @WebServiceServerTest 來測試使用 Spring Web Services 專案實現 Web 服務的應用。預設情況下,它配置一個 MockWebServiceClient bean,可用於呼叫您的 Web 服務端點。(更多關於在 Spring Boot 中使用 Web Services 的資訊,請參見Web Services。)

@WebServiceServerTest 啟用的自動配置設定列表在附錄中可以找到

以下示例展示了 @WebServiceServerTest 註解的使用方法

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.ws.test.server.RequestCreators;
import org.springframework.ws.test.server.ResponseMatchers;
import org.springframework.xml.transform.StringSource;

@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {

	@Autowired
	private MockWebServiceClient client;

	@Test
	void mockServerCall() {
		this.client
			.sendRequest(RequestCreators.withPayload(new StringSource("<ExampleRequest/>")))
			.andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
	}

}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest
import org.springframework.ws.test.server.MockWebServiceClient
import org.springframework.ws.test.server.RequestCreators
import org.springframework.ws.test.server.ResponseMatchers
import org.springframework.xml.transform.StringSource

@WebServiceServerTest(ExampleEndpoint::class)
class MyWebServiceServerTests(@Autowired val client: MockWebServiceClient) {

	@Test
	fun mockServerCall() {
		client
			.sendRequest(RequestCreators.withPayload(StringSource("<ExampleRequest/>")))
			.andExpect(ResponseMatchers.payload(StringSource("<ExampleResponse>42</ExampleResponse>")))
	}

}

額外的自動配置和切片

每個切片提供一個或多個 @AutoConfigure…​ 註解,它們主要定義了應包含在切片中的自動配置。可以透過建立自定義的 @AutoConfigure…​ 註解或透過將 @ImportAutoConfiguration 新增到測試中來逐個測試地新增額外的自動配置,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {

}
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
請確保不要使用常規的 @Import 註解匯入自動配置,因為 Spring Boot 以特定方式處理它們。

或者,可以透過在儲存在 META-INF/spring 中的檔案中註冊它們,為任何使用切片註解的地方新增額外的自動配置,如下例所示

META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
com.example.IntegrationAutoConfiguration

在此示例中,對於每個使用 @JdbcTest 註解的測試,都啟用了 com.example.IntegrationAutoConfiguration

您可以在此檔案中使用以 # 開頭的註釋。
只要切片或 @AutoConfigure…​ 註解元註解了 @ImportAutoConfiguration,就可以透過這種方式進行定製。

使用者配置和切片

如果組織您的程式碼的方式合理,您的 @SpringBootApplication預設被用作測試的配置。

因此,不要在應用的主類中散佈特定於其某個功能領域的配置設定變得很重要。

假設您正在使用 Spring Data MongoDB,依賴於其自動配置,並且已啟用審計。您可以如下定義您的 @SpringBootApplication

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {

	// ...

}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.data.mongodb.config.EnableMongoAuditing

@SpringBootApplication
@EnableMongoAuditing
class MyApplication {

	// ...

}

因為此類是測試的源配置,所以任何切片測試實際上都會嘗試啟用 Mongo 審計,這肯定不是您想要做的。建議的方法是將該領域特定的配置移到一個單獨的 @Configuration 類中,該類與您的應用處於同一級別,如下例所示

  • Java

  • Kotlin

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {

	// ...

}
import org.springframework.context.annotation.Configuration
import org.springframework.data.mongodb.config.EnableMongoAuditing

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
class MyMongoConfiguration {

	// ...

}
根據應用程式的複雜性,您可以選擇使用一個單獨的 @Configuration 類來包含所有自定義配置,或者按領域劃分,每個領域一個類。後一種方法允許您在需要時透過 @Import 註解在某個測試中啟用特定配置。有關何時可能需要為 slice test 啟用特定 @Configuration 類的更多詳細資訊,請參閱 本 how-to 部分

Slice test 會從掃描中排除 @Configuration 類。例如,對於 @WebMvcTest,以下配置不會將給定的 WebMvcConfigurer bean 包含在 slice test 載入的應用程式上下文中

  • Java

  • Kotlin

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {

	@Bean
	public WebMvcConfigurer testConfigurer() {
		return new WebMvcConfigurer() {
			// ...
		};
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {

	@Bean
	fun testConfigurer(): WebMvcConfigurer {
		return object : WebMvcConfigurer {
			// ...
		}
	}

}

然而,下面的配置將使自定義的 WebMvcConfigurer 由 slice test 載入。

  • Java

  • Kotlin

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

	// ...

}
import org.springframework.stereotype.Component
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Component
class MyWebMvcConfigurer : WebMvcConfigurer {

	// ...

}

另一個容易混淆的地方是 classpath 掃描。假設您以合理的方式組織了程式碼,但需要掃描額外的包。您的應用程式可能類似於以下程式碼

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

	// ...

}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.ComponentScan

@SpringBootApplication
@ComponentScan("com.example.app", "com.example.another")
class MyApplication {

	// ...

}

這樣做實際上覆蓋了預設的元件掃描指令,副作用是無論您選擇哪種 slice,都會掃描這兩個包。例如,@DataJpaTest 似乎突然掃描了應用程式的元件和使用者配置。同樣,將自定義指令移到一個單獨的類中是解決此問題的好方法。

如果這不適用於您,您可以在測試層次結構中的某個地方建立一個 @SpringBootConfiguration,以便它被替代使用。或者,您可以為測試指定一個源,這會停用查詢預設源的行為。

使用 Spock 測試 Spring Boot 應用程式

Spock 2.2 或更高版本可用於測試 Spring Boot 應用程式。為此,請在應用程式的構建中新增 Spock 的 spock-spring 模組的 -groovy-4.0 版本依賴。spock-spring 將 Spring 的測試框架整合到 Spock 中。有關更多詳細資訊,請參閱 Spock Spring 模組的文件