作ってみたライブラリ
KotlinでMockolo風のライブラリがあるか調べてみたが、なさそうだったので自分で作ってみた1。名前は、kotlin-simple-mockとした。
interfaceに@Mockableというアノテーションを付けると、そのMockを生成する。
GitHub
https://github.com/KamikazeZirou/kotlin-simple-mock
なぜライブラリを作ったか
普段、Kotlinでテストコードを書くときにmockKやMockitoを使っている。
これらのライブラリに大体満足しているのだが、一つだけ不満がある。
複雑でもなく、数も少ないテストケースを実行するときに、もっさり感があることだ。
mockKだと1秒以上かかることもざらである。Mockitoはmockより格段に高速だが、それでも100ms以上かかることはざらである。
同じような条件で、自前でテストモックを作ってテストを実行させると、100msもかからなかった。
高速なテスト実行を重視するのであれば、自前mockを作れば良いということだ。だが、mockをいちいち実装するのは面倒だ。
そこで思いあったのが、SwiftのMockoloというモック生成ライブラリだ。MockoloはProtocol2から単純なmockを高速に生成するライブラリだ。Kotlinでもこの方式のライブラリがあれば、Mockを自動生成しつつ、テストの実行時間を高速にできると考えた。
ざっくりとした使い方
Step 1. Mockを生成したいinterfaceを指定する
Mockを生成したいinterfaceに@Mockableを付与する。
import mock.simple.kotlin.Mockable
@Mockable
interface Hello {
fun add(a: Int, b: Int): Int
var num: Int
}
Step 2. ソースコードをビルドして、Mockを生成する
Mock生成にkaptを利用しているので、kaptを実行させるためにソースコードをビルドする。以下のようなコードがMockHello.ktとして生成される。
class MockHello : Hello {
public var addHandler: ((a: Int, b: Int) -> Int)? = null
public var addCallCount: Int = 0
public var addArgValues: MutableList<List<*>> = mutableListOf()
public override fun add(a: Int, b: Int): Int {
addCallCount += 1
addArgValues.add(listOf(a,b))
return addHandler!!(a,b)
}
public var underlyingNum: Int? = null
public var numSetCallCount: Int = 0
public override var num: Int
get() = underlyingNum!!
set(newValue) {
underlyingNum = newValue
numSetCallCount += 1
}
}
Step 3. テストコードを書く
class HelloTest {
@Test
fun add() {
// <メソッド名>FuncHandlerに関数を設定することで処理を差し替える
val mock = MockHello()
mock.addHandler = { a, b ->
a + b
}
val result = mock.add(1, 2)
assertEquals(3, result)
// <メソッド名>CallCountという変数で、メソッドの呼び出し回数を取得する
assertEquals(1, mock.addCallCount)
// <メソッド名>FuncArgValuesという変数で、メソッドが呼ばれたときの引数を取得する
assertEquals(listOf(1, 2), mock.addArgValues.first())
}
}
Mockライブラリのテスト実行時間の比較
https://qiita.com/zigenin/items/75679e1988b72dd728ac
(この記事の自前Mock = このライブラリで生成したMock)
最後に
ライブラリを作ったり、この記事をここまで書いておいてなんだが、Kotlinでテスト速度を追求したい人はMockitoを使うのが良いと思う。
初回のテスト実行時間はこのライブラリで短くなるが、kaptによってビルド時間が長くなることを考慮すると、Mockito使った方が良さそう3。