はじめに
なんやかんやで便利なMockkだけど割と情報が少ないので逆引きチートシートを自分用に用意
随時更新したいなぁ〜...
更新履歴
2019年9月20日 「特定の型が引数にセットされてメソッドが呼ばれたかを確認したい」を追加
2020年7月13日 「KotlinのCompanionなメソッドをMock化したい」を追加
あるメソッドをMock化したい
class SomeClass {
fun add(a:Int, b:Int) = a + b
}
fun someTest() {
val someClass = mockk<SomeClass>()
every { someClass.add(any(), any()) } returns 12
}
実装ポイント
- everyのカッコ内が対象のメソッドを書き、returnsで結果を定義
- 引数がある場合は指定できる。なんでもいい場合はany()、型を指定したい場合は
any<Int>()
的な感じ
テスト対象内にあるプライベートメソッドをMock化したい
class SomeTargetClass {
private fun minus(a:Int, b:Int) = a - b
}
@Test
fun someTest() {
val someTargetClass = spyk<SomeTargetClass>(recordPrivateCalls = true)
every { someTargetClass["minus"](any(), any()) } returns 12
}
実装ポイント
- テスト対象のクラスをspyk化する。これでテスト対象のクラスを操作できる
- spyk化する際に
recordPrivateCalls = true
を指定することでprivateメソッドにアクセスできるようになる - プライベートメソッドにアクセスするときは
インスタンス["メソッド名"](引数)
的な感じで書く
とりあえずMock化したクラスのメソッドはなんでもでいいからオブジェクト返して欲しい
メソッドが呼ばれてるかどうかを確認したい時に使える
コンストラクタ引数で指定されたパラメータのMock化が面倒な時に便利
class SomeClass {
fun add(a:Int, b:Int) = a + b
}
class SomeTargetClass constructor(private val someClass:SomeClass) {
fun minus(a:Int, b:Int) = someClass.add(a, -b)
}
@Test
fun someTest() {
val someClass = mockk<SomeClass>(relaxed = true)
val someTargetClass = SomeTargetClass(someClass)
someTargetClass.minus(1,2)
verify(exactly = 1) { someClass.add(any(), any()) }
}
実装ポイント
- mockk化する時に
relaxed = true
を指定する。これで指定したMockのレスポンスはデフォルト値を返すようになる
あるMock化したメソッドのレスポンス時に処理を挟みたい
class SomeTargetClass {
fun minus(a:Int, b:Int) = a - b
}
@Test
fun someTest() {
val someTargetClass = spyk<SomeTargetClass>(recordPrivateCalls = true)
every { someTargetClassminus(any(), any()) } answers {
println("mogemoge")
returns 12
}
}
実装ポイント
- everyに対して
returns
ではなくanswers
を使うことで処理を挟める
あるMock化したメソッドのレスポンス時に処理を挟む時に引数をいじりたい
関数引数をとるようなケースで使えると思うぞ
class CallBack {
val exec:(Int)->Unit
}
class SomeClass {
fun add(a:Int, b:Int, callBack:CallBack) {
val result = a + b
callBack.exec(result)
}
}
class SomeTargetClass constructor(private val someClass:SomeClass) {
fun minus(a:Int, b:Int) {
someClass.add(a, -b, CallBack({
printResult(it)
}))
}
fun printResult(result:Int) {
println(result)
}
}
@Test
fun someTest() {
val someClass = mockk<SomeClass>()
val someTargetClass = SomeTargetClass(someClass)
every { someClass.add(any(), any(), any()) answers {
thirdArg<CallBack>().exec(12)
}
someTargetClass.minus(1,2)
verify(exactly = 1) { printResult(12) }
}
実装ポイント
-
answers
の中ではthirdArg<型>()
で引数のデータにアクセスできる。firstArg
なら第1引数,secondArg
なら第2引数となっている。4つ目以降も同じ感じ
JavaのstaticなクラスをMock化したい
LocalDateTime.now()
のお供に
@Before
fun setup() {
mockkStatic(LocalDateTime::class)
every { LocalDateTime.now() } returns LocalDateTime.of(2020, 1, 1, 11, 11)
}
実装ポイント
-
mockkStatic
を使う
KotlinのCompanionなメソッドをMock化したい
class SomeCompanionObject {
companion object create(): SomeCompanionObject = SomeCompanionObject()
}
@Before
fun setup() {
val mock = mockk<SomeCompanionObject>()
mockkObject(SomeCompanionObject)
every { SomeCompanionObject.create() } returns mock
}
実装ポイント
-
mockkObject
を使う
変数のないメソッドをMock化したい
init時にInjectしてるような場合に便利かも
class SomeClass {
injector(someTargetClass:SomeTargetClass)
}
class SomeTargetClass constructor(private val someClass:SomeClass) {
init {
someClass.injector(this)
}
}
class SomeTargetClassTest {
private val someClass = mockk<SomeClass>()
@Before
fun setup() {
every { someClass.injector(any()) } just runs
}
}
実装ポイント
-
just runs
でUnitを返す関数を実行する感じ
特定の型が引数にセットされてメソッドが呼ばれたかを確認したい
ジェネリクスとかSealedクラス使って引数が複数の型を取りうる場合のテストに使えるぞ
class SomeTargetClass<T> {
fun print(t:T) = println(t.toString())
}
@Test
fun someTest() {
val someClass = SomeTargetClass<String>
someClass.print("abc")
verify { someClass.print(ofType<String>) }
}
実装ポイント
- 引数に
ofType<型>
と指定することで、指定した型でメソッドが引数として呼ばれたかが判別できる