LoginSignup
32
29

More than 3 years have passed since last update.

Mockk逆引きチートシート

Last updated at Posted at 2019-09-19

はじめに

なんやかんやで便利な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<型>と指定することで、指定した型でメソッドが引数として呼ばれたかが判別できる
32
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
29