Kotlin + Spring Boot + JUnit5 + MockKでUnitテストを書く

Kotlin + Spring Boot + JUnit5 + MockKでUnitテストを書く

Kotlin

前回実装したClean Architecture APIのUnitテストを書いていきます。

JUnit

JUnitとはJavaで開発されたプログラムにおいてユニットテストの自動化を行うためのフレームワークです。

よく使いそうなアノテーション

@Test

メソッドに付与することでJUnitにテストメソッドであることを認識します。

@DisplayName

テストの表示名を設定できます。

@BeforeEach

付与されたメソッドは、各テストメソッド実行前に@BeforeEachアノテーションがついたメソッドが実行されます。JUnit 4の@Beforeと同じです。

@Disabled

@Disabled はクラスまたはメソッドに付与してテストを実行させないようにすることができます。JUnit4における@Ignoreに相当します

JUnit5のアノテーション一覧は以下の公式で見ることができます。

https://oohira.github.io/junit5-doc-jp/user-guide/

 

MockK

MockKとは Kotlin用のモックライブラリです。

https://mockk.io/

every

everyを使うと特定のモックインスタンスが呼ばれた時に指定した値を返却します。

verify

verifyを使うとメソッドが指定した引数で呼び出されたかを確認できます。

よく使いそうなアノテーション

@MockK

モックインスタンスとしてインジェクションしたい場合に使用します。

@SpyK

Spy用のインスタンスとしてインジェクションしたい場合に使用します。

Spyはオブジェクトを実際のコードで動かしつつ、メソッドの引数や呼び出し回数、戻り値などを検証するために使用します。

注意点としては、アノテーションを利用して初期化する際には@Spyk var hoge = Hoge()のようにvarかつインスタンス生成する必要があります。

@InjectMockKs

該当オブジェクトのもつ属性に対してインジェクトしたい場合に使用します。

 

ControllerのUnitテスト

@ExtendWith(SpringExtension::class)でSpring拡張機能をインクルードしていましたが、調べたら@SpringBootTestアノテーションを使用している場合は@ExtendWith(SpringExtension::class)を付与する必要はなくなったようです。

参考:https://github.com/spring-projects/spring-boot/issues/13739

・TaskControllerTest.kt

 

HTTPメソッドへのテスト

MockMvcBuilders.standaloneSetup()でControllerの動作を再現するための準備をします。

参考:https://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/UnitTest/ImplementsOfUnitTest/UsageOfLibraryForTest.html#mockmvc

@BeforeEachアノテーションを付けて、全ての@Testの前に実行させます。以降、このmockMvcインスタンスを利用して、仮想のリクエストを発生させテストを実行します。

今回はテスト対象のTaskControllerを指定して、mockMvcを生成しています。

 

テストしたいPath(今回の場合はGET /api/tasks)を指定し、andExceptメソッドでレスポンスのテストを行います。

今回はHTTPステータスコードのテストなのでstatus()を使います。ステータスコード200はstatus().isOkでテストできます。代表的なステータスコードは以下の通りです。

  • 200: status().isOk()
  • 308: status().isPermanentRedirect()
  • 404: status().isNotFound()
  • 403: status().isForbidden()
  • 503: status().isServiceUnavailable()

あとはcontent()でレスポンスの形と中身を見ています。今回はREST APIなのでJSON形式であることJSONの中身が正しいことをアサートしています。

また、Controllerないのメソッドにおいて、該当のuseCaseメソッドが呼ばれたこともテストしています。

 

ドメインからJSONへの変換テスト

ControllerではUseCaseから返ってきたドメインをレスポンスの形であるJSONに変換しているため、そのテストも行っています。

今回は各ドメインにtoJson()というメソッドを生やしているので、そのメソッドを実行してちゃんとドメインからJSONに変換されているかをアサートしています。

assertEquals()で値が等しいかを確認しています。左が期待値で右が実測値を入れます。

 

UseCaseのUnitテスト

UseCaseのテストではPortのメソッドを呼び出して、その返り値が正しいかをアサートしています。今回は簡素なAPIなので特にUseCaseでロジック挟んでおらず、シンプルなUnitテストになっています。

・TaskUseCaseTest.kt

 

GatewayのUnitテスト

GatewayではDriverメソッドを呼び出し、その返り値をアサートしています。またGatewayではDriverで受け取ったプリミティブなJSONオブジェクトをドメインに変換する処理をしているため、変換メソッドのテストも追加しています。

・TaskGatewayTest.kt

 

MockKでlateinitプロパティが初期化されない問題

springのMockKだとうまくいくのにMockKだと以下のエラーが出る問題がありました。

調べてみるとinitメソッドで解決できそうでしたので、実際やってみたらうまくいきました。

参考:https://github.com/mockk/mockk/issues/546

以下のように@BeforeEachを追加するだけです。テスト実行前にMockKAnnotations.initでモックインスタンスをインジェクションすることで解決できます。