リクエストの実行

このセクションでは、MockMvcTester を使用してリクエストを実行する方法と、AssertJ と統合してレスポンスを検証する方法を説明します。

MockMvcTester は、静的メソッドをインポートする必要がないことを除いて、Hamcrest サポートと同じ MockHttpServletRequestBuilder を再利用するリクエストを作成するための流れるような API を提供します。返されるビルダーは 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"))
	. // ...

次の例に示すように、クエリまたはフォームパラメーターを表すサーブレットリクエストパラメーターを追加することもできます。

  • Java

  • Kotlin

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

アプリケーションコードがサーブレットリクエストパラメーターに依存しており、クエリ文字列を明示的にチェックしない場合(ほとんどの場合)、使用するオプションは関係ありません。ただし、URI テンプレートで提供されるクエリパラメーターはデコードされますが、param(…​) メソッドを介して提供されるリクエストパラメーターはすでにデコードされていることが予想されます。

非同期

リクエストの処理が非同期で行われる場合、exchange() はリクエストの完了を待機し、アサートする結果が実質的に不変になるようにします。デフォルトのタイムアウトは 10 秒ですが、次の例に示すように、リクエストごとに制御できます。

  • Java

  • Kotlin

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

生の結果を取得し、非同期リクエストのライフサイクルを自分で管理したい場合は、exchange ではなく asyncExchange を使用します。

マルチパート

内部的に 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)))
	. // ...

サーブレットとコンテキストパスの使用

ほとんどの場合、コンテキスト URI とサーブレットパスはリクエスト 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 は重要ではありません。リクエストごとに指定する必要があるためです。