LoginSignup
117
71

[Kotlin] MockKまとめ

Last updated at Posted at 2019-10-15

はじめに

MockKとは Kotlin用のモックライブラリ です。

この記事では公式ドキュメントを日本語に訳し、自分用に使い方をまとめたものになります。これから辞書代わりに使っていこうと考えています。
私自身まだ実践で使ったことない機能もありますので、ニュアンス違うよなどの指摘やアドバイスを頂けると幸いです。

インストレーション(Installation)

始めるために必要なことは、MockKライブラリに依存関係を追加することです。

Gradle / Mavenの依存関係(Gradle/maven dependency)

アプローチ 命令
Gradle testImplementation "io.mockk:mockk:{version}"
Gradle(Kotlin DSL) testImplementation("io.mockk:mockk:{version}")
Unit testImplementation "io.mockk:mockk:{version}"
Instrumented androidTestImplementation "io.mockk:mockk-android:{version}"
Common multiplatform testImplementation "io.mockk:mockk-common:{version}"

{version}は以下のバージョンに対応します。

  • Kotlin 1.3+およびCoroutines 1.0+バージョン
  • Kotlin 1.2互換バージョン

DSLの例(DSL examples)

最も単純な例です。デフォルトではモックは厳密なので、何らかの動作を提供する必要があります。

val car = mockk<Car>()

every { car.drive(Direction.NORTH) } returns Outcome.OK

car.drive(Direction.NORTH) // returns OK

verify { car.drive(Direction.NORTH) }

confirmVerified(car)

アノテーション(Annotations)

アノテーションを使用して、モックオブジェクトの作成を簡素化できます。

class TrafficSystem {
  lateinit var car1: Car
  
  lateinit var car2: Car
  
  lateinit var car3: Car
}

class CarTest {
  @MockK
  lateinit var car1: Car

  @RelaxedMockK
  lateinit var car2: Car

  @MockK(relaxUnitFun = true)
  lateinit var car3: Car

  @SpyK
  var car4 = Car()
  
  @InjectMockKs
  var trafficSystem = TrafficSystem()
  
  @Before
  fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true) // すべてのモックに対してrelaxUnitFunをオンにします

  @Test
  fun calculateAddsValues1() {
      // ... use car1, car2, car3 and car4
  }
}

インジェクションは、最初にプロパティを名前で照合し、次にクラスまたはスーパークラスで照合します。カスタマイズのためにlookupTypeパラメータを確認してください。

privateが適用されている場合でも、プロパティが注入されます。インジェクションのコンストラクターは、引数の最大数から最小数まで選択されます。

デフォルトでは、@InjectMockKsは、lateinit varまたは割り当てられていないvarのみを挿入します。これを変更するには、overrideValues = trueを使用します。これは、すでに何らかの方法で初期化されている場合でも値を割り当てます。valを注入するには、injectImmutable = trueを使用します。短い表記の場合、デフォルトで@InjectMockKsと同じことを行う@OverrideMockKsを使用しますが、この2つのフラグをオンにします。

JUnit5

JUnit5では、MockKExtensionを使用してモックを初期化できます。

@ExtendWith(MockKExtension::class)
class CarTest {
  @MockK
  lateinit var car1: Car

  @RelaxedMockK
  lateinit var car2: Car

  @MockK(relaxUnitFun = true)
  lateinit var car3: Car

  @SpyK
  var car4 = Car()

  @Test
  fun calculateAddsValues1() {
      // ... use car1, car2, car3 and car4
  }
}

さらに、テスト関数のパラメーターで@MockKおよび@RelaxedMockKを使用する可能性が追加されます。

@Test
fun calculateAddsValues1(@MockK car1: Car, @RelaxedMockK car2: Car) {
  // ... use car1 and car2
}

Spy

スパイはモックと実際のオブジェクトを混ぜることができます。

val car = spyk(Car()) // またはspyk <Car>()でデフォルトのコンストラクターを呼び出します

car.drive(Direction.NORTH) // Carが返す実際の関数を返します

verify { car.drive(Direction.NORTH) }

confirmVerified(car)

注:スパイオブジェクトは、渡されたオブジェクトのコピーです。

Relaxed mock

relaxed mockは、すべての関数に対して単純な値を返すモックです。これにより、各ケースの動作の指定をスキップしながら、必要なものをスタブ化できます。参照型の場合、連鎖モックが返されます。

val car = mockk<Car>(relaxed = true)

car.drive(Direction.NORTH) // returns null

verify { car.drive(Direction.NORTH) }

confirmVerified(car)

注:relaxed mockを使用する際は、戻り値がジェネリックではうまく機能しません。通常、この場合、クラスキャスト例外がスローされます。戻り値がジェネリックの場合、スタブを手動で指定する必要があります。

回避策:

val func = mockk<() -> Car>(relaxed = true) // この場合、invoke関数は戻り値にジェネリックを持ちます

// この行は回避策です。この行がないと`relaxed mock`は次の行でクラスキャスト例外をスローします
every { func() } returns Car() // または、例えばmockk()を返すことができます

func()

Unitを返す関数のRelaxed mock(Mock relaxed for functions returning Unit)

Unitを返す関数をRelaxed mockにしたい場合は、mockk関数、@MockK アノテーションまたはMockKAnntations.init関数の引数としてrelaxUnitFun = trueを使用できます。

mockk関数:

mockk<MockCls>(relaxUnitFun = true)

@MockK アノテーション:

@MockK(relaxUnitFun = true)
lateinit var mock1: RurfMockCls
init {
    MockKAnnotations.init(this)
}

MockKAnntations.init関数:

@MockK
lateinit var mock2: RurfMockCls
init {
    MockKAnnotations.init(this, relaxUnitFun = true)
}

オブジェクトモック(Object mocks)

オブジェクトは次の方法でモックに変換できます。

object MockObj {
  fun add(a: Int, b: Int) = a + b
}

mockkObject(MockObj) // モックをオブジェクトに適用します

assertEquals(3, MockObj.add(1, 2))

every { MockObj.add(1, 2) } returns 55

assertEquals(55, MockObj.add(1, 2))

元に戻すには、unmockkAllまたはunmockkObjectを使用します。

@Before
fun beforeTests() {
    mockkObject(MockObj)
    every { MockObj.add(1,2) } returns 55
}

@Test
fun willUseMockBehaviour() {
    assertEquals(55, MockObj.add(1,2))
}

@After
fun afterTests() {
    unmockkAll()
    // or unmockkObject(MockObj)
}

Kotlin言語の制限にもかかわらず、テストロジックで必要な場合は、オブジェクトの新しいインスタンスを作成できます。

val newObjectMock = mockk<MockObj>()

クラスモック(Class mock)

時には、任意のクラスのモックが必要です。そのような場合にはmockkClassを使用してください。

val car = mockkClass(Car::class)

every { car.drive(Direction.NORTH) } returns Outcome.OK

car.drive(Direction.NORTH) // returns OK

verify { car.drive(Direction.NORTH) }

列挙モック(Enumeration mocks)

列挙型は、mockkObjectを使用してモックできます。

enum class Enumeration(val goodInt: Int) {
    CONSTANT(35),
    OTHER_CONSTANT(45);
}

mockkObject(Enumeration.CONSTANT)
every { Enumeration.CONSTANT.goodInt } returns 42
assertEquals(42, Enumeration.CONSTANT.goodInt)

コンストラクタモック(Constructor mocks)

場合によっては、特に所有していないコードでは、新しく作成したオブジェクトをモックする必要があります。この目的のために、次の構成が提供されます。

class MockCls {
  fun add(a: Int, b: Int) = a + b
}

mockkConstructor(MockCls::class)

every { anyConstructed<MockCls>().add(1, 2) } returns 4

assertEquals(4, MockCls().add(1, 2)) // 新しいオブジェクトが作成されることに注意してください

verify { anyConstructed<MockCls>().add(1, 2) }

基本的な考え方は、モックされたクラスのコンストラクタが実行された直後に、オブジェクトはconstructed mockになるということです。そのようなモックのモッキング動作は、anyConstructed<MockCls>()で示される特別なprototype mockに接続されます。このようなprototype mockのクラスごとに1つのインスタンスがあります。コールの記録は、prototype mockでも発生します。関数の動作が指定されていない場合、元の関数が実行されます。

部分引数マッチング(Partial argument matching)

通常の引数とマッチャーの両方を混在させることができます。

val car = mockk<Car>()

every { 
  car.recordTelemetry(
    speed = more(50),
    direction = Direction.NORTH, // ここで `eq()` が使用されます
    lat = any(),
    long = any()
  )
} returns Outcome.RECORDED

obj.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142)

verify { obj.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142) }

confirmVerified(obj)

連鎖呼び出し(Chained calls)

呼び出しのチェーンをスタブ化できます。

val car = mockk<Car>()

every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP

car.door(DoorType.FRONT_LEFT) // Doorの連鎖モックを返します
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP

verify { car.door(DoorType.FRONT_LEFT).windowState() }

confirmVerified(car)

注:関数の戻り値の型がジェネリックである場合、実際の型に関する情報は消去されます。連鎖呼び出しを機能させるには、追加情報が必要です。ほとんどの場合、フレームワークはキャスト例外をキャッチし、autohinting を実行します。明示的に必要な場合は、次の呼び出しを行う前にhintを使用してください。

every { obj.op2(1, 2).hint(Int::class).op1(3, 4) } returns 5

階層的なモック(Hierarchical mocking)

バージョン1.9.1から、モックを階層にチェーンすることができます。

interface AddressBook {
    val contacts: List<Contact>
}

interface Contact {
    val name: String
    val telephone: String
    val address: Address
}

interface Address {
    val city: String
    val zip: String
}

val addressBook = mockk<AddressBook> {
    every { contacts } returns listOf(
        mockk {
            every { name } returns "John"
            every { telephone } returns "123-456-789"
            every { address.city } returns "New-York"
            every { address.zip } returns "123-45"
        },
        mockk {
            every { name } returns "Alex"
            every { telephone } returns "789-456-123"
            every { address } returns mockk {
                every { city } returns "Wroclaw"
                every { zip } returns "543-21"
            }
        }
    )
}

キャプチャ(Capturing)

CapturingSlotまたはMutableListへの引数をキャプチャできます。

val car = mockk<Car>()

val slot = slot<Double>()
val list = mutableListOf<Double>()

every {
  obj.recordTelemetry(
    speed = capture(slot),
    direction = Direction.NORTH
  )
} answers {
  println(slot.captured)

  Outcome.RECORDED
}


every {
  obj.recordTelemetry(
    speed = capture(list),
    direction = Direction.SOUTH
  )
} answers {
  println(list.captured())

  Outcome.RECORDED
}

obj.recordTelemetry(speed = 15, direction = Direction.NORTH) // prints 15
obj.recordTelemetry(speed = 16, direction = Direction.SOUTH) // prints 16

verify(exactly = 2) { obj.recordTelemetry(speed = or(15, 16), direction = any()) }

confirmVerified(obj)

最小、最大、または正確な回数の検証(Verification atLeast, atMost or exactly times)

呼び出しカウントは、atLeastatMost、またはexactlyパラメーターで確認できます。

val car = mockk<Car>(relaxed = true)

car.accelerate(fromSpeed = 10, toSpeed = 20)
car.accelerate(fromSpeed = 10, toSpeed = 30)
car.accelerate(fromSpeed = 20, toSpeed = 30)

// すべて通ります
verify(atLeast = 3) { car.accelerate(allAny()) }
verify(atMost  = 2) { car.accelerate(fromSpeed = 10, toSpeed = or(20, 30)) }
verify(exactly = 1) { car.accelerate(fromSpeed = 10, toSpeed = 20) }
verify(exactly = 0) { car.accelerate(fromSpeed = 30, toSpeed = 10) } // 呼び出しが実行されなかったことを意味します

confirmVerified(car)

検証順序(Verification order)

  • verifyAllは、順序を確認せずにすべての呼び出しが行われたことを検証します。
  • verifySequenceは、指定された順序で呼び出しが行われたことを検証します。
  • verifyOrderは、呼び出しが特定の順序で発生したことを検証します。
  • wasNot Calledは、モックまたはモックのリストがまったく呼び出されなかったことを検証します。
class MockedClass {
    fun sum(a: Int, b: Int) = a + b
}

val obj = mockk<MockedClass>()
val slot = slot<Int>()

every {
    obj.sum(any(), capture(slot))
} answers {
    1 + firstArg<Int>() + slot.captured
}

obj.sum(1, 2) // returns 4
obj.sum(1, 3) // returns 5
obj.sum(2, 2) // returns 5

verifyAll {
    obj.sum(1, 3)
    obj.sum(1, 2)
    obj.sum(2, 2)
}

verifySequence {
    obj.sum(1, 2)
    obj.sum(1, 3)
    obj.sum(2, 2)
}

verifyOrder {
    obj.sum(1, 2)
    obj.sum(2, 2)
}

val obj2 = mockk<MockedClass>()
val obj3 = mockk<MockedClass>()
verify {
    listOf(obj2, obj3) wasNot Called
}

confirmVerified(obj)

検証確認(Verification confirmation)

verify...構文によってすべての呼び出しが検証されたことを再確認するには、confirmVerifiedを使用できます。

confirmVerified(mock1, mock2)

これらの検証メソッドは、検証付きのすべての呼び出しを網羅しているため、verifySequenceおよびverifyAllに使用することはあまり意味がありません。

いくつかの呼び出しが検証なしで残っている場合、例外をスローします。

一部の呼び出しはこのような確認からスキップされる場合があります。詳細については次のセクションを確認してください。

val car = mockk<Car>()

every { car.drive(Direction.NORTH) } returns Outcome.OK
every { car.drive(Direction.SOUTH) } returns Outcome.OK

car.drive(Direction.NORTH) // returns OK
car.drive(Direction.SOUTH) // returns OK

verify {
    car.drive(Direction.SOUTH)
    car.drive(Direction.NORTH)
}

confirmVerified(car) // すべての呼び出しが検証でカバーされたことを確認します

記録の除外(Recording exclusions)

それほど重要ではない呼び出しを記録から除外するには、excludeRecordsを使用できます。

excludeRecords { mock.operation(any(), 5) }

一致するすべての呼び出しは記録から除外されます。これは、verifyAllverifySequenceconfirmVerifiedなどの包括的な検証を使用している場合に役立ちます。

val car = mockk<Car>()

every { car.drive(Direction.NORTH) } returns Outcome.OK
every { car.drive(Direction.SOUTH) } returns Outcome.OK

excludeRecords { car.drive(Direction.SOUTH) }

car.drive(Direction.NORTH) // returns OK
car.drive(Direction.SOUTH) // returns OK

verify {
    car.drive(Direction.NORTH)
}

confirmVerified(car) // car.drive(Direction.SOUTH)が除外されたため、car.drive(Direction.NORTH)のみで確認できます。

検証タイムアウト(Verification timeout)

同時操作を確認するには、timeout = xxxを使用できます。

mockk<MockCls> {
    every { sum(1, 2) } returns 4

    Thread {
        Thread.sleep(2000)
        sum(1, 2)
    }.start()

    verify(timeout = 3000) { sum(1, 2) }
}

これは、検証に合格するか、タイムアウトに達するかのいずれかの状態になるまで待機します。

Unitを返却(Returning Unit)

関数がUnitを返す場合、just Runsを使用できます。

class MockedClass {
    fun sum(a: Int, b: Int): Unit {
        println(a + b)
    }
}

val obj = mockk<MockedClass>()

every { obj.sum(any(), 3) } just Runs

obj.sum(1, 1)
obj.sum(1, 2)
obj.sum(1, 3)

verify {
    obj.sum(1, 1)
    obj.sum(1, 2)
    obj.sum(1, 3)
}

コルーチン(Coroutines)

コルーチンをモックするには、サポートライブラリに別の依存関係を追加する必要があります。

Gradle

testCompile "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x"

Maven

<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-core</artifactId>
    <version>x.x</version>
    <scope>test</scope>
</dependency>

次に、coEverycoVerifycoMatchcoAssertcoRuncoAnswers、またはcoInvokeを使用して、中断関数をモックできます。

val car = mockk<Car>()

coEvery { car.drive(Direction.NORTH) } returns Outcome.OK

car.drive(Direction.NORTH) // returns OK

coVerify { car.drive(Direction.NORTH) }

拡張関数(Extension functions)

拡張関数には3つのケースがあります。

  • クラス全体(class wide)
  • オブジェクト全体(object wide)
  • モジュール全体(module wide)

オブジェクトとクラスの場合、通常のモックを作成するだけで拡張関数をモックできます。

data class Obj(val value: Int)

class Ext {
    fun Obj.extensionFunc() = value + 5
}

with(mockk<Ext>()) {
    every {
        Obj(5).extensionFunc()
    } returns 11

    assertEquals(11, Obj(5).extensionFunc())

    verify {
        Obj(5).extensionFunc()
    }
}

モジュール全体の拡張機能をモックするには、モジュールのクラス名を引数としてmockkStatic(...)をビルドする必要があります。例えば、pkgパッケージのモジュールFile.ktの"pkg.FileKt"。

data class Obj(val value: Int)

// File.kt("pkg" パッケージ)で宣言されています
fun Obj.extensionFunc() = value + 5

mockkStatic("pkg.FileKt")

every {
    Obj(5).extensionFunc()
} returns 11

assertEquals(11, Obj(5).extensionFunc())

verify {
    Obj(5).extensionFunc()
}

@JvmNameを使用する場合は、クラス名として指定します。

KHttp.kt:

@file:JvmName("KHttp")

package khttp
// ... KHttp code 

テストコード:

mockkStatic("khttp.KHttp")

拡張関数をモックするために、もう少し詳しく知る必要がある場合があります。たとえば、File.endsWith()拡張関数には、まったく予測できないclassnameがあります。

mockkStatic("kotlin.io.FilesKt__UtilsKt")
every { File("abc").endsWith(any<String>()) } returns true
println(File("abc").endsWith("abc"))

これは、予測不能な標準的なKotlinの動作です。[Tools]-> [Kotlin]-> [Show Kotlin Bytecode]を使用するか、JARアーカイブ内の.classファイルを確認して、このような名前を検出します。

可変引数(Varargs)

バージョン1.9.1から、さらに拡張された可変引数処理が可能になりました。

    interface ClsWithManyMany {
        fun manyMany(vararg x: Any): Int
    }

    val obj = mockk<ClsWithManyMany>()

    every { obj.manyMany(5, 6, *varargAll { it == 7 }) } returns 3

    println(obj.manyMany(5, 6, 7)) // 3
    println(obj.manyMany(5, 6, 7, 7)) // 3
    println(obj.manyMany(5, 6, 7, 7, 7)) // 3

    every { obj.manyMany(5, 6, *anyVararg(), 7) } returns 4

    println(obj.manyMany(5, 6, 1, 7)) // 4
    println(obj.manyMany(5, 6, 2, 3, 7)) // 4
    println(obj.manyMany(5, 6, 4, 5, 6, 7)) // 4

    every { obj.manyMany(5, 6, *varargAny { nArgs > 5 }, 7) } returns 5

    println(obj.manyMany(5, 6, 4, 5, 6, 7)) // 5
    println(obj.manyMany(5, 6, 4, 5, 6, 7, 7)) // 5

    every {
        obj.manyMany(5, 6, *varargAny {
            if (position < 3) it == 3 else it == 4
        }, 7)
    } returns 6
    
    println(obj.manyMany(5, 6, 3, 4, 7)) // 6
    println(obj.manyMany(5, 6, 3, 4, 4, 7)) // 6

プライベート関数のモック / 動的呼び出し(Private functions mocking / dynamic calls)

プライベート関数をモックする必要がある場合は、動的呼び出しを介して行うことができます。

class Car {
    fun drive() = accelerate()

    private fun accelerate() = "going faster"
}

val mock = spyk<Car>(recordPrivateCalls = true)

every { mock["accelerate"]() } returns "going not so fast"

assertEquals("going not so fast", mock.drive())

verifySequence {
    mock.drive()
    mock["accelerate"]()
}

プライベート呼び出しを検証する場合は、recordPrivateCalls = truespykを作成する必要があります。

さらに、より詳細な構文により、同じ動的呼び出しと組み合わせてプロパティを取得および設定できます。

val mock = spyk(Team(), recordPrivateCalls = true)

every { mock getProperty "speed" } returns 33
every { mock setProperty "acceleration" value less(5) } just runs
every { mock invokeReturnsUnit "privateMethod" } just runs
every { mock invoke "openDoor" withArguments listOf("left", "rear") } returns "OK"

verify { mock getProperty "speed" }
verify { mock setProperty "acceleration" value less(5) }
verify { mock invoke "openDoor" withArguments listOf("left", "rear") }

プロパティバッキングフィールド(Property backing fields)

fieldValueを介してフィールドバッキングプロパティにアクセスし、設定される値にvalueを使用できます。

注:以下の例では、propertyTypeを使用してfieldValueの型を指定しています。これは、ゲッターのタイプを自動的にキャプチャできるため、必要です。 nullablePropertyTypeを使用して、null許容型を指定します。

val mock = spyk(MockCls(), recordPrivateCalls = true)

every { mock.property } answers { fieldValue + 6 }
every { mock.property = any() } propertyType Int::class answers { fieldValue += value }
every { mock getProperty "property" } propertyType Int::class answers { fieldValue + 6 }
every { mock setProperty "property" value any<Int>() } propertyType Int::class answers  { fieldValue += value }
every {
    mock.property = any()
} propertyType Int::class answers {
    fieldValue = value + 1
} andThen {
    fieldValue = value - 1
}

複数のインターフェース(Multiple interfaces)

インターフェイスを介して追加の動作を追加し、それらをスタブ化します。

val spy = spyk(System.out, moreInterfaces = Runnable::class)

spy.println(555)

every {
    (spy as Runnable).run()
} answers {
    (self as PrintStream).println("Run! Run! Run!")
}

val thread = Thread(spy as Runnable)
thread.start()
thread.join()

Mocking nothing

ここでは特別なことは何もありません。 Nothingを返す関数がある場合:

fun quit(status: Int): Nothing {
    exitProcess(status)
}

次に、たとえば、例外を動作としてスローできます。

every { quit(1) } throws Exception("this is a test")

マッチャーの拡張性(Matcher extensibility)

非常に簡単な方法は、関数をMockKMatcherScopeまたはMockKVerificationScopeにアタッチし、match関数を使用して新しいマッチャーを作成することです。

fun MockKMatcherScope.seqEq(seq: Sequence<String>) = match<Sequence<String>> {
    it.toList() == seq.toList()
}

また、Matcherインターフェースを実装することにより、より高度なマッチャーを作成することもできます。

設定ファイル(Settings file)

パラメータをグローバルに調整するには、リソースファイルでいくつかの設定を指定します。

使い方:

  1. リソースにio/mockk/settings.propertiesファイルを作成します。
  2. 次のオプションのいずれかを入力します。
relaxed=true|false
relaxUnitFun=true|false
recordPrivateCalls=true|false

DSLテーブル(DSL tables)

DSLを習得するのに役立ついくつかの表を次に示します。

トップレベル関数(Top level functions)

関数 説明
mockk<T>(...) 通常のモックを作成します
spyk<T>() デフォルトのコンストラクタを使用してスパイを構築します
spyk(obj) objからコピーしてスパイを構築します
slot キャプチャスロットを作成します
every スタブブロックを開始します
coEvery コルーチンのスタブブロックを開始します
verify 検証ブロックを開始します
coVerify コルーチンの検証ブロックを開始します
verifyAll すべての呼び出しを含む検証ブロックを開始します
coVerifyAll コルーチンのすべての呼び出しを含む検証ブロックを開始します
verifyOrder 順序を確認する検証ブロックを開始します
coVerifyOrder コルーチンの順序を確認する検証ブロックを開始します
verifySequence すべての呼び出しが指定された順序で行われたかどうかを確認する検証ブロックを開始します
coVerifySequence コルーチンのすべての呼び出しが指定された順序で行われたかどうかを確認する検証ブロックを開始します
excludeRecords 呼び出しから一部の記録を除外する
confirmVerified 記録されたすべての呼び出しが検証されたことを確認します
clearMocks 指定されたモックをクリアします
registerInstanceFactory 特定のオブジェクトのインスタンス化の方法を再定義できます
mockkClass クラスをパラメーターとして渡すことにより、通常のモックを作成します
mockkObject 任意のオブジェクトをオブジェクトのモックにするか、すでに変換されている場合はクリアします
unmockkObject オブジェクトモックを通常のオブジェクトに戻します
mockkStatic クラスから静的モックを作成するか、既に変換されている場合はクリアします
unmockkStatic 静的モックを通常のクラスに戻します
clearStaticMockk 静的モックをクリアします
mockkConstructor コンストラクタモックをクラスから外すか、すでに変換されている場合はクリアします
unmockkConstructor コンストラクタモックを通常のクラスに戻します
clearConstructorMockk コンストラクターのモックをクリアします
unmockkAll オブジェクトモック、静的モック、コンストラクタモックをアンモックします
clearAllMocks 通常のモック、オブジェクトモック、静的モック、コンストラクタモックをクリアします

マッチャー(Matchers)

デフォルトでは、単純な引数はeq()を使用して照合されます。

マッチャー 説明
any() 任意の引数に一致します
allAny() 単純な引数として提供されるマッチャーにeq()の代わりにany()を使用する特別なマッチャーです
isNull() 値がnullかどうかをチェックします
isNull(inverse=true) 値がnullでないかどうかをチェックします
ofType(type) 値が型に属するかどうかを確認します
match { it.startsWith("string") } 渡された述部を介して一致します
coMatch { it.startsWith("string") } 渡されたコルーチン述部を介して一致します
matchNullable { it?.startsWith("string") } 渡された述部を介してnull許容値に一致します
coMatchNullable { it?.startsWith("string") } 渡されたコルーチン述部を介してnull許容値に一致します
eq(value) 値がdeepEquals関数を介して提供された値と等しい場合に一致します
eq(value, inverse=true) 値がdeepEquals関数を介して提供された値と等しくない場合に一致します
neq(value) 値がdeepEquals関数を介して提供された値と等しくない場合に一致します
refEq(value) 値が参照比較によって提供された値と等しい場合に一致します
refEq(value, inverse=true) 値が参照比較によって提供された値と等しくない場合に一致します
nrefEq(value) 値が参照比較によって提供された値と等しくない場合に一致します
cmpEq(value) 値がcompareTo関数で提供された値と等しい場合に一致します
less(value) 値がcompareTo関数で指定された値より小さい場合に一致します
more(value) 値がcompareTo関数で提供された値より大きい場合に一致します
less(value, andEquals=true) 値がcompareTo関数で指定された値以下である場合に一致します
more(value, andEquals=true) 値がcompareTo関数で指定された値以上である場合に一致します
range(from, to, fromInclusive=true, toInclusive=true) compareTo関数を介して値が範囲内にある場合に一致します
and(left, right) 論理積で2つのマッチャーを結合します
or(left, right) 論理和で2つのマッチャーを結合します
not(matcher) マッチャーを無効にします
capture(slot) CapturingSlotに値をキャプチャします
capture(mutableList) 値をリストにキャプチャします
captureNullable(mutableList) null値とともに値をリストにキャプチャします
captureLambda() ラムダをキャプチャします
captureCoroutine() コルーチンをキャプチャします
invoke(...) 一致した引数を呼び出します
coInvoke(...) コルーチンの一致した引数を呼び出します
hint(cls) 消去された場合に備えて、次の戻り値のタイプを示します
anyVararg() 可変引数の任意の要素に一致します
varargAny(matcher) いずれかの要素がマッチャーに一致する場合に一致します
varargAll(matcher) すべての要素がマッチャーに一致する場合に一致します
any...Vararg() 可変引数のすべての要素に一致します(プリミティブ型に固有)
varargAny...(matcher) いずれかの要素がマッチャーに一致する場合に一致します(プリミティブ型に固有)
varargAll...(matcher) すべての要素がマッチャーに一致する場合に一致します(プリミティブ型に固有)

検証モードでのみ使用可能な特別なマッチャーがいくつかあります。

マッチャー 説明
withArg { code } 任意の値に一致し、コードの実行を許可します
withNullableArg { code } null値を許可する値と一致し、コードの実行を許可します
coWithArg { code } 任意の値に一致し、コルーチンコードを実行できます
coWithNullableArg { code } null値を許可する値と一致し、コルーチンコードを実行できます

バリデーター(Validators)

バリデーター 説明
verify { mock.call() } 呼び出しが実行されたことを順不同で検証します
verify(inverse=true) { mock.call() } 呼び出しが実行されなかったことを順不同で検証します
verify(atLeast=n) { mock.call() } 呼び出しが少なくともn回実行されたことを順不同で検証します
verify(atMost=n) { mock.call() } 呼び出しが最大n回実行されたことを順不同で検証します
verify(exactly=n) { mock.call() } 呼び出しが正確にn回実行されたことを順不同で検証します
verifyAll { mock.call1(); mock.call2() } 言及されたモックに対して指定された呼び出しのみが実行されたことを順不同で検証します
verifyOrder { mock.call1(); mock.call2() } 指定した順序で呼び出しが次々に行われたことを検証します
verifySequence { mock.call1(); mock.call2() } 言及されたモックに対して、指定された一連の呼び出しのみが実行されたことを検証します
verify { mock wasNot Called } モックが呼び出されなかったことを検証します
verify { listOf(mock1, mock2) wasNot Called } モックのリストが呼び出されなかったことを検証します

アンサー(Answers)

アンサーの後に、1つ以上の追加のアンサーが続く場合があります。

アンサー 説明
returns value 一致した呼び出しが指定された値を返すことを指定します
returnsMany list 一致した呼び出しがリストから値を返し、後続の呼び出しが次の要素を返すことを指定します
throws ex 一致した呼び出しが例外をスローすることを指定します
answers { code } 一致した呼び出しがanswer scopeを持つコードブロックで応答することを指定します
coAnswers { code } 一致した呼び出しがanswer scopeを持つコルーチンコードブロックで応答することを指定します
answers answerObj 一致した呼び出しがAnswerオブジェクトで応答することを指定します
answers { nothing } 一致した呼び出しがnullに応答することを指定します
just Runs 一致した呼び出しがUnitを返すことを指定します(nullを返します)
propertyType Class バッキングフィールドアクセサの型を指定します
nullablePropertyType Class バッキングフィールドアクセサの型をnull許容型として指定します

追加のアンサー(Additional answer)

結果として生じる各呼び出しで次のアンサーが返され、最後の値が保持されます。これは、returnsManyセマンティクスに似ています。

追加のアンサー 説明
andThen value 一致した呼び出しが1つの指定された値を返すことを指定します
andThenMany list 一致した呼び出しがリストから値を返し、次の要素が返されるたびに返すことを指定します
andThenThrows ex 一致した呼び出しが例外をスローすることを指定します
andThen { code } 一致した呼び出しがanswer scopeでスコープされたコードブロックで応答することを指定します
coAndThen { code } 一致した呼び出しがanswer scopeでスコープされたコルーチンコードブロックで応答することを指定します
andThenAnswer answerObj 一致した呼び出しがAnswerオブジェクトで応答することを指定します
andThen { nothing } 一致した呼び出しがnullに応答することを指定します

アンサースコープ(Answer scope)

パラメータ 説明
call 呼び出しとマッチャーで構成される呼び出しオブジェクト
invocation 呼び出された実際の関数に関する情報が含まれています
matcher 呼び出しの照合に使用されるマッチャーに関する情報が含まれています
self 呼び出されたオブジェクトを参照します
method 呼び出された関数を参照します
args 呼び出す引数を参照します
nArgs 呼び出す引数の数
arg(n) n番目の引数
firstArg() 第一引数
secondArg() 第二引数
thirdArg() 第三引数
lastArg() 最後の引数
captured() リストにキャプチャするときに便利なリストの最後の要素
lambda<...>().invoke() キャプチャされたラムダを呼び出す
coroutine<...>().coInvoke() キャプチャされたコルーチンを呼び出す
nothing 答えとして何も返さない場合はnull値
fieldValue プロパティバッキングフィールドへのアクセサ
fieldValueAny Any?型を持つプロパティバッキングフィールドへのアクセサ
value プロパティバッキングフィールドと同じ型にキャストされる値
valueAny Any?型で設定されている値

可変引数スコープ(Vararg scope)

パラメータ 説明
position 可変引数配列の引数の位置
nArgs 可変引数配列の引数の総数

おわりに

公式ドキュメントでは使用例が掲載されていない機能も多数あるので、GitHubのサンプルコードからも引っ張ってこようと思ってます。

終盤、心が折れそうになりました。
スラスラとUnit Test書けるようになりたい。(大抵なにかしらで躓いてます...)

117
71
2

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
117
71