日々是好日

プログラミングについてのあれこれ、ムダ知識など

Mockito+kotlin-allopenプラグインでモックする

Kotlinでテストを書く場合、kotlin-allopenプラグインを使うと簡単にモックできるということですが、ネットに転がってる情報が思いのほか不親切だった(他責)ので個人的まとめ。

結論

プロジェクトレベルbuild.gradleappレベルbuild.gradleに追記し、アノテーションクラスAllOpen.ktを作る。

// プロジェクトレベルbuild.gradle
buildScript {
...

  dependencies {
    ...
    classpath "org.jetbrains.kotlin:kotlin-allopen:${versions.kotlin}" // 追記
  }
}
// appレベルbuild.gradle
...
apply plugin: 'kotlin-allopen' // 追記

... // android とか dependencies とか

// 追記
allOpen {
  // 次に作るAllOpenアノテーションのパス
  annotation("<your package>.AllOpen")
}
/* <your package>/AllOpen.kt */
// src/main/package/annotationパッケージとか切って配置
package <your package>

@Target(AnnotationTarget.CLASS) // ← 書かなくてもおk
annotation class AllOpen

あとはモックするクラスに@AllOpenアノテーションを付与するだけ。

@AllOpen
class TargetClass {}

kotlin-allopenとは

つ公式 kotlinlang.org

Kotlinではコンパイル後のクラスに全てfinal修飾子が付くため、Mockitoを使ったユニットテストでモックする場合にエラーを吐く。

Cannot mock/spy class com.your.package.ExampleClass
Mockito cannot mock/spy because :
 - final class

上記で定義したアノテーション@AllOpenをモック対象のクラスに付与することで、コンパイル後のクラスに強制的にopen修飾子を付与してくれるとのこと。

リリースビルドではどうするのよ

テストとリリースとで分ける処理をどこかで見たけど埋もれてしまった。←

mock-maker-inlineは?

Androidテスト全書より。

// src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker を作成
mock-maker-inline // この一行だけを記述

なお、MockMakerを利用した方法では前出のAllOpenを使った方法に比べて、かなりパフォーマンス上の不利益があることが報告されています。どちらの方法を選択するかはパフォーマンスと手間との兼ね合いで判断してください。

ということなので、AllOpenアノテーションを付与する方法を採用しました。