以前は Kotlintest と呼ばれたテストフレームワークが Kotest と名前を変えて開発が進められています。
このテストフレームワークは非常に多様なテストの記述方法をサポートしており、テストケースのスタイルからアサーションメソッド、パラメトライズドテストのサポートまでテストに必要な幅広い関心をカバーしています。 マルチプラットフォームでの動作をサポートしたテストフレームワークとなっていて、自分で拡張を導入するための構造も持っています。公式にも様々な拡張が提供されていて、Robolectric と一緒にテストを動作させるための拡張も用意されています。 ただし2020年10月現在 Robolectric の拡張は experimental であり、うまく動作しないパターンが多々あります。この記事では、次に示す各ライブラリのバージョンを組み合わせて kotest を Android アプリプロジェクトに導入する方法を紹介します。
- kotest: 4.2.5
- kotest-robolectric-extension: 4.0.1
- Robolectric: 4.4
依存関係
build.gradle に下記の記述を加え、kotest および Robolectric を導入します。
android{ testOptions { unitTests.all { useJUnitPlatform() } } } dependencies { // kotest testImplementation 'io.kotest:kotest-runner-junit5:4.2.5' testImplementation 'io.kotest:kotest-assertions-core:4.2.5' testImplementation 'io.kotest:kotest-property:4.2.5' testImplementation 'io.kotest:kotest-extensions-robolectric-jvm:4.0.1' // robolectric testImplementation 'org.robolectric:robolectric:4.4' }
必要に応じて、テストの source directory 配下に resources ディレクトリを掘って、robolectric.properties
ファイルを作ります。
sdk=28 application=your.app.pkg.YourApp
kotest-robolectric-extension による Robolectric の有効化
テストの source directory の好きなパッケージに ProjectConfig
オブジェクトを作ります。
ProjectConfig
オブジェクトは様々な extension を追加したり、テストの並列数を制御したりするためのオブジェクトです。 Robolectric を kotest で動作できるようにするには、extensions で RobolectricExtension
を含むリストを返します。
また、Robolectric を使う場合の制約として、IsolationMode を InstancePerLeaf
にしておく必要があります。IsolationMode はテストケースのインスタンスをどの単位でもつかを制御するパラメータで、InstancePerLeaf がもっとも細かい単位でインスタンスを作ります。現状の kotest robolectric extension による Robolectric の初期化処理では、モジュール内に複数のテストを記述したファイルがある場合、Looper の初期化処理が複数回呼ばれてしまい例外が投げられることがあります。IDE では個別にテストを実行するため気付きにくいですが、CLI で一気にテストを実行するとこのエラーに遭遇します。InstancePerLeaf
ではテストケースのインスタンスごとに Robolectric の設定も異なる扱いをうけるためか、この初期化処理の重複による失敗を回避できます。
object ProjectConfig : AbstractProjectConfig() { override val isolationMode: IsolationMode = InstancePerLeaf override fun extensions(): List<Extension> = listOf(RobolectricExtension()) }
ちなみに、筆者が試した限り Robolectric を有効化したモジュールで ProjectConfig
の parallelism を 2 以上にした場合も Robolectric の内部状態が不正になりテストが実行できなくなりました。
テストの記述
kotest-robolectric-extension で Robolectric を有効化したモジュールでは、自分が確認した限り次のテストスタイルが利用可能です。これら以外を使用すると Robolectric の初期化処理が複数回呼ばれてテストが失敗します(とくに BehaviorSpec
や DescribeSpec
、FeatureSpec
)。
StringSpec
WordSpec
FunSpec
Robolectric を利用するテストには @RobolectricTest
アノテーションを付けます。
が、現状の kotest-robolectric-extension では同じモジュール内で Robolectric を利用しなくてもよいテストを記述している場合でも、@RobolectricTest
アノテーションを付けておかないとテストが実行できない(テストケースがひとつも実行されず正常終了する)ため、Robolectric が不要なテストにも @RobolectricTest
アノテーションを付与することになります。
@RobolectricTest class SomeTest : StringSpec() { init { "something" { // Given: Values val a = 0 val b = 1 // When: Subtract val subtracted = a - b // Then: subtracted value should be -1 subtracted.shouldBe(-1) } } }
マルチモジュールプロジェクトでの kotest の設定
先に紹介した ProjectConfig
はモジュールごとに定義します。マルチモジュールな状況では kotest を使うすべてのモジュールで ProjectConfig
オブジェクトを作る必要があります。少々面倒ではありますが、これにより、Robolectric を使うモジュールとそうでないモジュールを切り分けることができるため、Robolectric が必要なモジュールでは StringSpec
を用い、そうでないモジュールでは BehaviorSpec
を用いるなどのスタイルの使い分けがしやすくなります。