執行請求

本節展示如何使用 MockMvcTester 執行請求及其與 AssertJ 的整合以驗證響應。

MockMvcTester 提供了一個流暢的 API 來構建請求,它重用了與 Hamcrest 支援相同的 MockHttpServletRequestBuilder,只是無需匯入靜態方法。返回的構建器是 AssertJ 感知的,因此將其包裝在常規的 assertThat() 工廠方法中會觸發交換,並提供對 MvcTestResult 的專用斷言物件的訪問。

下面是一個簡單的例子,它對 /hotels/42 執行 POST,並配置請求以指定一個 Accept

  • Java

  • Kotlin

assertThat(mockMvc.post().uri("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON))
		. // ...
assertThat(mockMvc.post().uri("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON))
	. // ...

AssertJ 通常包含多個 assertThat() 語句來驗證交換的不同部分。除了像上面那樣使用單個語句,您還可以使用 .exchange() 返回一個 MvcTestResult,該物件可以在多個 assertThat 語句中使用

  • Java

  • Kotlin

MvcTestResult result = mockMvc.post().uri("/hotels/{id}", 42)
		.accept(MediaType.APPLICATION_JSON).exchange();
assertThat(result). // ...
val result = mockMvc.post().uri("/hotels/{id}", 42)
	.accept(MediaType.APPLICATION_JSON).exchange()
assertThat(result)
	. // ...

您可以透過 URI 模板樣式指定查詢引數,示例如下

  • Java

  • Kotlin

assertThat(mockMvc.get().uri("/hotels?thing={thing}", "somewhere"))
		. // ...
assertThat(mockMvc.get().uri("/hotels?thing={thing}", "somewhere"))
	. // ...

您還可以新增代表查詢引數或表單引數的 Servlet 請求引數,示例如下

  • Java

  • Kotlin

assertThat(mockMvc.get().uri("/hotels").param("thing", "somewhere"))
		. // ...
assertThat(mockMvc.get().uri("/hotels").param("thing", "somewhere"))
	. // ...

如果應用程式程式碼依賴 Servlet 請求引數並且不顯式檢查查詢字串(大多數情況下都是如此),那麼使用哪種選項都無關緊要。但是請記住,透過 URI 模板提供的查詢引數會被解碼,而透過 param(…​) 方法提供的請求引數則被假定已解碼。

非同步

如果請求的處理是非同步的,exchange() 會等待請求完成,這樣要斷言的結果就是有效的不可變的。預設超時時間是 10 秒,但可以根據每個請求進行控制,示例如下

  • Java

  • Kotlin

assertThat(mockMvc.get().uri("/compute").exchange(Duration.ofSeconds(5)))
		. // ...
assertThat(mockMvc.get().uri("/compute").exchange(Duration.ofSeconds(5)))
	. // ...

如果您更喜歡獲取原始結果並自己管理非同步請求的生命週期,請使用 asyncExchange 而不是 exchange

多部分

您可以執行檔案上傳請求,這些請求內部使用 MockMultipartHttpServletRequest,因此實際上沒有對多部分請求進行解析。相反,您必須像以下示例那樣進行設定

  • Java

  • Kotlin

assertThat(mockMvc.post().uri("/upload").multipart()
		.file("file1.txt", "Hello".getBytes(StandardCharsets.UTF_8))
		.file("file2.txt", "World".getBytes(StandardCharsets.UTF_8)))
	. // ...
assertThat(mockMvc.post().uri("/upload").multipart()
		.file("file1.txt", "Hello".toByteArray(StandardCharsets.UTF_8))
		.file("file2.txt", "World".toByteArray(StandardCharsets.UTF_8)))
	. // ...

使用 Servlet 和上下文路徑

在大多數情況下,最好將上下文路徑和 Servlet 路徑排除在請求 URI 之外。如果您必須使用完整的請求 URI 進行測試,請務必相應地設定 contextPathservletPath,以便請求對映正常工作,示例如下

  • Java

  • Kotlin

assertThat(mockMvc.get().uri("/app/main/hotels/{id}", 42)
		.contextPath("/app").servletPath("/main"))
		. // ...
assertThat(mockMvc.get().uri("/app/main/hotels/{id}", 42)
		.contextPath("/app").servletPath("/main"))
	. // ...

在前面的示例中,為每個執行的請求設定 contextPathservletPath 會很麻煩。相反,您可以設定預設請求屬性,示例如下

  • Java

  • Kotlin

MockMvcTester mockMvc = MockMvcTester.of(List.of(new HotelController()),
		builder -> builder.defaultRequest(get("/")
				.contextPath("/app").servletPath("/main")
				.accept(MediaType.APPLICATION_JSON)).build());
val mockMvc =
	MockMvcTester.of(listOf(HotelController())) { builder: StandaloneMockMvcBuilder ->
		builder.defaultRequest<StandaloneMockMvcBuilder>(
			MockMvcRequestBuilders.get("/")
				.contextPath("/app").servletPath("/main")
				.accept(MediaType.APPLICATION_JSON)
		).build()
	}

上述屬性會影響透過 mockMvc 例項執行的每個請求。如果某個給定請求也指定了相同的屬性,則它會覆蓋預設值。這就是為什麼預設請求中的 HTTP 方法和 URI 無關緊要的原因,因為它們必須在每個請求中都指定。

© . This site is unofficial and not affiliated with VMware.