執行請求
本節說明如何使用 MockMvcTester
執行請求,以及其與 AssertJ 的整合以驗證回應。
MockMvcTester
提供流暢的 API 來組合請求,該請求重複使用與 Hamcrest 支援相同的 MockHttpServletRequestBuilder
,但不需要匯入靜態方法。傳回的 Builder 具有 AssertJ 感知能力,因此將其包裝在常規 assertThat()
工廠方法中會觸發交換,並提供對 MvcTestResult
專用 Assert 物件的存取權。
以下是一個簡單的範例,在 /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()
傳回可在多個 assertThat
語句中使用的 MvcTestResult
-
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
。
Multipart
您可以執行檔案上傳請求,這些請求在內部使用 MockMultipartHttpServletRequest
,因此實際上沒有 multipart 請求的解析。相反,您必須將其設定為類似於以下範例
-
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 和 Context 路徑
在大多數情況下,最好將 Context 路徑和 Servlet 路徑排除在請求 URI 之外。如果您必須使用完整請求 URI 進行測試,請務必相應地設定 contextPath
和 servletPath
,以便請求對應能夠運作,如下列範例所示
-
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"))
. // ...
在前面的範例中,為每個執行的請求設定 contextPath
和 servletPath
會很麻煩。相反,您可以設定預設請求屬性,如下列範例所示
-
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 無關緊要的原因,因為它們必須在每個請求上指定。