1. 概要

このクイックチュートリアルでは、SpringFramework5.2以降で利用可能な新しいKotlin固有のMockMvcサポートについて説明します。

注:バージョン5.2はまだGAではないため、SpringMilestoneリポジトリを使用する必要があります。

2. テストするコントローラー

テストするコントローラーをセットアップしましょう

次のドメインを使用します。

// Payload
data class Name(val first: String, val last: String)

// Web request
data class Request(val name: Name) 

// Web response
@JsonInclude(JsonInclude.Include.NON_NULL) data class Response(val error: String?)

そして、受信したペイロードを検証するRESTコントローラー:

@RestController
@RequestMapping("/mockmvc")
class MockMvcController {
 
    @RequestMapping(value = ["/validate"], method = [RequestMethod.POST], 
      produces = [MediaType.APPLICATION_JSON_VALUE])
    fun validate(@RequestBody request: Request): Response {
        val error = if (request.name.first == "admin") {
            null
        } else {
            ERROR
        }
        return Response(error)
    }
 
    companion object {
        const val ERROR = "invalid user"
    }
}

ここでは、JSONにシリアル化されたカスタム Request クラスのインスタンスを受け取り、JSONにシリアル化されたカスタムResponseクラスのインスタンスを返すPOSTエンドポイントがあります。

3. 従来のテストアプローチ

標準のMockMvcアプローチを使用して、上記のコントローラーをテストできます。

mockMvc.perform(MockMvcRequestBuilders
  .post("/mockmvc/validate")
  .accept(MediaType.APPLICATION_JSON)
  .contentType(MediaType.APPLICATION_JSON)
  .content(mapper.writeValueAsString(Request(Name("admin", "")))))
  
  .andExpect(MockMvcResultMatchers.status().isOk)
  .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
  .andExpect(MockMvcResultMatchers.content().string("{}"))

上記の例では、標準のテストサポートを使用して、いくつかの側面を検証しました。

  • HTTP応答コードは200です
  • 応答のContent-Typeはapplication/jsonです。
  • 応答コンテンツは空のJSONオブジェクトです

実際、それは非常に見栄えがよく、クリーンで表現力豊かです。 ただし、 KotlinDSLを使用するとさらにクリーンにできます。

4. 最新のテストアプローチ

同じテストを次のように書き直すことができます。

mockMvc.post("/mockmvc/validate") {
  contentType = MediaType.APPLICATION_JSON
  content = mapper.writeValueAsString(Request(Name("admin", "")))
  accept = MediaType.APPLICATION_JSON
}.andExpect {
    status { isOk }
    content { contentType(MediaType.APPLICATION_JSON) }
    content { json("{}") }
}

これがどのように実装されているかを確認しましょう。 まず、Spring Framework 5.2+を使用する必要があります。これには、 MockMvcExtensions.kt — MockMvcのカスタムDSLが含まれています。

まず、拡張関数 MockMvc.post() MockHttpServletRequestDsl拡張メソッドで呼び出します。

mockMvc.post("/mockmvc/validate") {
    // This is extension method's body
}

これは、メソッドがMockHttpServletRequestDslオブジェクトをthis参照として実行されることを意味します。したがって、 contentType content 、およびacceptプロパティ。

次に、同様の方法で期待値を定義します— MockMvcResultMatchersDsl拡張メソッドを提供します。

andExpect {
    // Extension method body
}

5. さらなる進化

さらにテストを追加したい場合は、重複を避けるために既存のコードをリファクタリングできます。

ここで一般的なコードを便利なdoTestメソッドに抽出してみましょう。

private fun doTest(input: Request, expectation: Response) {
    mockMvc.post("/mockmvc/validate") {
      contentType = MediaType.APPLICATION_JSON
      content = mapper.writeValueAsString(input)
      accept = MediaType.APPLICATION_JSON
    }.andExpect {
      status { isOk }
      content { contentType(MediaType.APPLICATION_JSON) }
      content { json(mapper.writeValueAsString(expectation)) }
    }
}

これで、実際のテストが簡略化されます。

@Test
fun `when supported user is given then validation is successful`() {
    doTest(Request(Name("admin", "")), Response(null))
}

@Test
fun `when unsupported user is given then validation is failed`() {
    doTest(Request(Name("some-name", "some-surname")), Response(MockMvcController.ERROR))
}

6. 結論

この記事では、 MockMvc KotlinDSLを使用してテストコードをよりクリーンにする方法を確認しました。 これは特効薬ではなく、テストコードをさらに簡潔にする小さな機能です。

これはカスタムDSLの使用法のもう1つの例であるため、他のプロジェクトでもカスタムDSLの使用を開始する動機として役立ちます。

いつものように、この記事の完全なソースコードは、GitHubから入手できます。