Mockとは何か、モックオブジェクトとテスト用ライブラリ

著者: IT Sectr 公開日: 2026-04-10 読了時間: 8 分

Mockとは、実際のコンポーネントの動作を模倣し、そのコンポーネントとの相互作用を検証できるようにする代替オブジェクトです。単に所定の値を返すStubとは異なり、Mockはメソッドの呼び出し、渡された引数、および呼び出し回数を記録します。Mockito (2024)によると、MockはJavaおよびKotlinプロジェクトで最も人気のあるTest Doubleタイプであり、モバイルアプリケーションのユニットテストの70%以上で使用されています。

重要ポイント

  • Mock — 相互作用を検証するオブジェクト:どのメソッドが、どの引数で、何回呼ばれたかを確認
  • Mockito — JavaおよびAndroidプロジェクトでMockを作成する最も人気のあるライブラリ
  • MockK — コルーチンとシールドクラスをネイティブサポートするKotlin向けMockitoの代替
  • Behavior verification — MockとStubの主な違い:Mockは状態ではなく振る舞いを検証する
  • Over-mocking — 主要なアンチパターン:mockは外部依存関係にのみ使用すべき

Mockとは?

Mockは、モッキングフレームワーク(Mockito、MockK、EasyMock)によって作成されるオブジェクトで、インターフェースやクラスをシミュレートし、そのメソッドへのすべての呼び出しを記録します。開発者は期待値を設定します:メソッドXが引数Yで呼び出され、Zを返す。テスト実行後、Mockは期待値が実際の呼び出しと一致したかを検証します。

この用語はTest Doublesの演劇のメタファーに由来します:Mockは(Dummyのように)単にステージに立つだけでなく、役割を演じ、自分との相互作用が正しかったかを検証する「模倣者」です。テスト対象のコードがMockの期待するメソッドを呼び出さなかった場合、または間違った引数で呼び出した場合 — テストは期待値違反のメッセージで失敗します。

Mockの仕組み

Mockはフレームワークのファクトリを通じて作成されます:mockk<MyInterface>()またはMockito.mock(MyClass.java)。フレームワークはすべてのメソッド呼び出しをインターセプトするプロキシオブジェクトを生成します。各呼び出しは事前に定義された期待値と比較されます。呼び出しが期待値と一致する場合は指定された値が返され、一致しない場合は設定に応じてデフォルト値が返されるか例外がスローされます。

Mockが必要な場合

Mockは、テスト対象のコードが副作用を持つコンポーネント(サーバーへのデータ送信、データベースへの書き込み、ロギング、アナリティクス、ナビゲーション、システムダイアログの表示など)と相互作用する場合に不可欠です。Mockなしでは、実際のインフラストラクチャを実行せずにこれらの相互作用を検証することはできません。Google Testing Blogによると、Mockはテストサーバーを起動せずにアプリケーションが実際にアナリティクスイベントを送信したことを検証する唯一の方法です。

Mock vs Stub:詳細比較

MockStubの違いは、テストにおいて最も議論されるトピックの一つです。どちらのタイプも実際の依存関係を置き換えますが、根本的に異なる方法で行います。

基準MockStub
主な質問メソッドは呼び出されたか?どの結果が返されたか?
検証振る舞い(verify)状態(assert)
データ返却オプション必須
verify(analytics).logEvent(“click”)assertEquals(5, repository.getCount())
使用時副作用データ返却

実用的なルール:Mockか否か

判断のための簡単なテスト:「このコード行を削除したら、テストは失敗するか?」と自問してください。テストが戻り値を検証する場合はStub(assertベースの検証)が必要です。テストがコードが正しい引数でメソッドを呼び出したかを検証する場合はMock(verifyベースの検証)が必要です。この二分法はCommand-Query Separationパターンに従います:状態を変更するメソッド(commands)はMockを必要とし、データを返すメソッド(queries)はStubを必要とします。

Mockito vs MockK:ライブラリ比較

MockitoMockKの選択は、KotlinでAndroidプロジェクトのテストスタックを設定する際の最初の判断の一つです。どちらのライブラリも同じ目的を果たしますが、Kotlin固有の機能に対するアプローチが異なります。

Mockito:実績ある古典

MockitoはJavaプロジェクトの事実上の標準です。バージョン5.xは、組み込みのMockMakerにより、finalクラス、staticメソッド、コンストラクタのモッキングをサポートしています。Kotlinプロジェクトの場合、Mockitoには追加のセットアップが必要です:構文改善のためのmockito-kotlin拡張、finalクラスのためのmockito-inline。Mockitoは追加のアダプタなしではKotlinコルーチンとsuspend関数をサポートしません。

MockK:Kotlinファーストのアプローチ

MockKはKotlin向けに特別に作成されました。コルーチン(coEverycoVerify)、シールドクラス、データクラス、オブジェクトシングルトン、拡張関数をネイティブサポートします。MockKの構文はラムダブロックを使用したDSLを使用し、Kotlinコードで自然に見えます。MockKは追加設定なしでプロパティモッキングもサポートしており、これはLiveData、StateFlow、Delegatesを使用するAndroidプロジェクトにとって重要です。

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

パフォーマンス比較

ベンチマーク(JVM Benchmark、2024)によると、MockKはJava ReflectionsではなくKotlinバイトコードを直接操作するため、KotlinプロジェクトにおいてMockitoよりも15–20%高速にモックオブジェクトを作成します。数千のユニットテストを持つプロジェクトでは、ビルド速度の差が顕著になります:MockKは大規模プロジェクトのテストフル実行で30–60秒を節約します。

KotlinでのMockテスト例

3つのシナリオを見てみましょう:Mock依存関係を使用したViewModelのテスト、API呼び出しを検証するUseCaseのテスト、coVerifyを使用したコルーチンのテストです。

例1:Mockアナリティクスを使用したViewModel

kotlin
class ProfileViewModelTest {
    private val analytics = mockk<AnalyticsService>()
    private val repo = mockk<UserRepository>()
    private val vm = ProfileViewModel(repo, analytics)

    fun `profile opened logs analytics event`() {
        every { analytics.logEvent("profile_opened") } returns Unit

        vm.onViewCreated()

        verify { analytics.logEvent("profile_opened") }
    }
}

例2:非同期検証を使用したUseCase

kotlin
class SendMessageUseCaseTest {
    private val api = mockk<MessagingApi>()
    private val useCase = SendMessageUseCase(api)

    fun `send message with correct payload`() = runTest {
        val message = Message(text = "Hello", userId = 42)

        coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")

        val result = useCase.execute(message)

        coVerify {
            api.sendMessage(match {
                it.text == "Hello" && it.userId == 42
            })
        }
        assertTrue(result is MessageResult.Sent)
    }
}

例3:ArgumentCaptorを使用した引数検証

kotlin
class OrderUseCaseTest {
    private val api = mockk<OrderApi>()
    private val useCase = OrderUseCase(api)
    private val slot = slot<OrderRequest>()

    fun `order request contains correct items`() = runTest {
        coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")

        useCase.execute(listOf("item_a", "item_b"))

        assertEquals(2, slot.captured.items.size)
        assertEquals("item_a", slot.captured.items[0])
    }
}

Mockテストのベストプラクティス

モバイル開発におけるMockの効果的な使用には規律が必要です。これらのルールに違反すると、テストはリファクタリングのたびに壊れる脆い障害物に変わります。

アプリケーションの外部境界のみMock化

厳格なルール:Mockはアプリケーション境界を越える依存関係に対してのみ作成すべきです:APIクライアント、データベース、ファイルシステム、システムサービス(LocationManager、BluetoothAdapter、Camera)。内部アプリケーションクラス — ドメインエンティティ、Value Objects、単純なユーティリティ — はMockに置き換えるべきではありません。それらの振る舞いは実際のオブジェクトを通じてテストされます。

1テストに1つのassert / verify

各テストは、verify(Mockの場合)またはassert(Stubの場合)のいずれか、ちょうど1つの論理チェックを含むべきです。1つのテストで状態と振る舞いの検証を混在させないでください。API呼び出しとその結果の両方をチェックする必要がある場合は、別々の名前で2つの別々のテストを作成してください。このルールは、「1テストに1つのassert」として知られ、Kent Beck(2002)の推奨に由来します。

  • Unitを返すメソッドにはMockKでrelaxUnitFun = trueを使用 — そうしないと、未指定の呼び出しでMockが例外をスローします
  • verifyは重要な呼び出しのみに制限 — すべてのgetterやsetterを検証しないでください。テストが脆くなります
  • ArgumentMatchersを適切に適用 — 引数がビジネスロジックにとって重要な場合、any()は重要な詳細を隠します
  • verifyNoMoreInteractionsの過剰使用を避ける — このメソッドはプロダクションコードの変更に対してテストを不必要に硬直させます
  • @MockkAnnotationsを使用してMockオブジェクトを自動初期化 — ボイラープレートを削減し、可読性を向上させます

Mockテストの高度なテクニック

基本的なモッキングに加えて、モバイル開発における特定のタスクを解決する高度なテクニックがあります:マルチスレッドのテスト、Flow状態の検証、実際のオブジェクトの部分モッキングです。

spyKを使用した部分Mock

Spy(または部分mock)を使用すると、実際の実装に呼び出しを委譲しつつ、個別のメソッドをオーバーライドできるオブジェクトを作成できます。MockKでは、spykは実際のクラスインスタンスに基づいて作成されます:val repo = spyk(InMemoryUserRepository())everyで定義された期待値を持つ呼び出しはMockを通過し、残りは実際のオブジェクトを通過します。Spyは、依存性注入がまだ実装されていないレガシーコードのテストで、1つのメソッドだけをオーバーライドする必要がある場合に特に便利です。

Turbineを使用したStateFlowのテスト

Jetpack Composeを使用する最新のAndroidプロジェクトでは、ViewModelはStateFlowを通じて状態を公開します。MockKはFlow依存関係のモッキングを可能にし、Turbineライブラリは出力検証を簡素化します。古典的なパターン:Flowを返すUseCaseにはMockK、ViewModelの出力検証にはTurbine。このスタックは、Kotlin Coroutinesを使用するプロジェクトに対してAndroid Testingドキュメント(Google、2024)で推奨されています。

kotlin
class SearchViewModelTest {
    private val searchUseCase = mockk<SearchUseCase>()
    private val vm = SearchViewModel(searchUseCase)

    fun `search emits results`() = runTest {
        coEvery { searchUseCase.search("android") } returns
            flowOf(SearchResult.Success(listOf(Item("Android TDD"))))

        vm.search("android")

        vm.state.test {
            val state = awaitItem()
            assertTrue(state.items.isNotEmpty())
            cancelAndIgnoreRemainingEvents()
        }
    }
}

よくある質問

MockとMockitoの違いは何ですか?

Mockは概念であり、振る舞いを検証するTest Doubleの一種です。MockitoはJavaおよびAndroidでMockオブジェクトを作成するためのライブラリです。他のライブラリ:MockK(Kotlin)、EasyMock(Java)、Cuckoo(iOS)。

MockはKotlinコルーチンとどのように動作しますか?

Mockでsuspend関数をテストするには、MockK(coEvery / coVerify)またはmockito-kotlinを使用したMockitoを使用します。MockKはコルーチンをネイティブサポートします:coEveryはsuspend関数の動作を定義し、coVerifyはコルーチン内での呼び出しを検証します。すべてのsuspend呼び出しはrunTest(kotlinx-coroutines-test)内で実行する必要があります。

Mockは繰り返し呼び出しで異なる値を返せますか?

はい。MockKではreturnsManyを使用します:every { api.getData() } returnsMany listOf(response1, response2)MockitoではthenReturn(value1).thenReturn(value2)のチェーンを使用します。これは連続した呼び出しで異なる応答を返す動作のテストに便利です。

テスト間でMockの状態をクリアするには?

MockKでは、relaxed = trueを指定した@MockKアノテーションを使用し、@AfterメソッドでclearMocks(mock)を呼び出します。MockitoではMockito.reset(mock)を使用します。ベストプラクティス:@Beforeでテストごとに新しいMockを作成し、テスト間の干渉を排除します。

MockはKotlinのシールドクラスをどのように扱いますか?

MockKはシールドクラスで正しく動作します:every { useCase() } returns Result.Success(data)Mockitoはシールドクラスを直接サポートしておらず、回避策が必要です。これがKotlinプロジェクトでMockitoよりもMockKが推奨される理由の一つです。

まとめ

  • Mock — 依存関係の状態(assert)ではなく振る舞い(verify)を検証するTest Doubleタイプ
  • Mockito — Java/Androidの標準、MockK — コルーチンとシールドクラスをサポートするKotlinファーストの選択肢
  • 基本ルール:外部境界(ネットワーク、DB、システムサービス)にはMock、内部クラスには実際のオブジェクト
  • Over-mocking — 主要なアンチパターン:過剰な依存関係の置き換えはテストを脆くし役に立たなくする
  • 1テスト — 1論理チェック:Mockの場合はverify、Stubの場合はassert、同じテストで両方は不可
  • ArgumentCaptor / slot — 盲目的なany()の代わりにMock呼び出しの引数を検証する正しい方法
  • MockKはKotlinプロジェクトに推奨:coEverycoVerifyは追加アダプタなしでコルーチンとネイティブに動作

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください