【正社員】還元率83%【フリーランス】マージン一律5万円で案件をご紹介させていただきます。 詳細はこちら

【Android】<Kotlin>MockKを使ったJUnit5テスト完全ガイド|ViewModelとRepositoryのモック実践

【Android】<Kotlin>MockKを使ったJUnit5テスト完全ガイド|ViewModelとRepositoryのモック実践
すだ

みなさまこんにちは〜!
メモリアインクのすだです。

今回は、Androidアプリ開発におけるJUnit5を使ったテストにおいて
MockKをどのように利用するかについて解説していきます。

この記事を読んでわかること…
・MockKとは?「モック」の必要性
・MockKの基本構文
・MockKを使った実践的なテスト方法

JUnit5を使用した基本的なテストの方法に関しては、以下の記事で詳しくご紹介しています。
ぜひ併せてご覧ください。

目次

環境

  • Kotlin (ver 1.9.0)
  • Android Studio (Giraffe | 2022.3.1 Patch 3)

MockKとは?

ユニットテスト(単体テスト)では、「テスト対象以外の部分を置き換える仕組み(=モック)」が重要です。
たとえば、以下のようなケースです:

  • ViewModelの中でRepositoryを使っている
  • でもテスト時には、Repositoryの中身(DBやAPI通信)は動かしたくない

こういうときに「モック」が便利です。

MockKは、Kotlin専用のモックライブラリなので モックの作成や管理に非常に役立ちます。
https://mockk.io/

MockKの導入時は、以下をGradleに追加してください。

testImplementation("io.mockk:mockk:1.13.5")

MockKの使い方の基礎

1. モックの生成

val repository = mockk<UserRepository>()

mockk<オブジェクトの型>() と書くことで、
指定したクラス(型)の「偽物インスタンス(モック)」を作る宣言をすることができます。

通常の UserRepository ならAPI通信やDBアクセスをしてしまいますが、
モックにすることで テスト時にそれらを実行せず、好きな戻り値を返せます。

2. モックの挙動を定義(every)

every { repository.fetchUserName() } returns "テストユーザー"

every { ... } は「この呼び出しがあったときに」
returns ... は「この値を返してね」という意味です。
つまり、モックがどう振る舞うかを事前にプログラムしておく構文です。

ここでは、repository.fetchUserName() がテスト中に呼ばれたら"テストユーザー" を返すようにモックの振る舞いを指定しています。

この every { ... } returns ... がないと、モックは何も返せません。
テスト中に呼ばれても null や例外が返ってしまいます。

3. 実際に呼び出されたかを確認(verify)

verify { repository.fetchUserName() }

verify { … }と書くことで、
「本当にこの関数が呼ばれたか?」を確認して、処理が期待通りに動いたか検証することができます。

ここでは、fetchUserName() が1回は呼ばれたことをチェックしており
もしこれが仮に呼ばれていなかったとするとテスト失敗になりエラーが出力されます。

MockKの基本構文を使ったテスト全体像

基本構文を使ったテストコードの全体像は以下です。

テスト対象のViewModel(例)▼

class ProfileViewModel(private val repository: UserRepository) {
    fun getGreeting(): String {
        val name = repository.fetchUserName()
        return "こんにちは、${name} さん!"
    }
}

テストコード全体(MockK+JUnit5)▼

class ProfileViewModelTest {

    @Test
    fun `ユーザー名が反映された挨拶が返る`() {
        // モックの生成
        val repository = mockk<UserRepository>()
        // モックの挙動を定義(この関数が呼ばれたらこの値を返す)
        every { repository.fetchUserName() } returns "サトウ"

        // ViewModelにモックを注入
        val viewModel = ProfileViewModel(repository)
        // テスト対象の関数を実行
        val result = viewModel.getGreeting()

        // 結果の確認(アサーション)
        assertEquals("こんにちは、サトウ さん!", result)

        // 実際に呼び出されたかを確認(検証)
        verify { repository.fetchUserName() }
    }
}

モックの生成と挙動の定義が完了したら、
生成したモックを ProfileViewModel の引数に渡し、テストの中で getGreeting() を呼び出します。

assertEquals を使ってメソッドの返り値が期待通りかどうかを検証し、
さらに verify { ... } を使うことで、モックの関数が実際に呼び出されたかを確認できます。

アノテーションを使ったモックの定義(@MockK)

MockKでは mockk<>() を使う以外にも、アノテーション(@MockK)を使ってモックを生成する方法もあります。

これは、JUnitの @BeforeEach などと組み合わせることで、
複数のモックを自動的に初期化できる便利なスタイルです。

class ProfileViewModelTest {

    @MockK
    lateinit var repository: UserRepository

    lateinit var viewModel: ProfileViewModel

    @BeforeEach
    fun setUp() {
        // モックを初期化
        MockKAnnotations.init(this)
        viewModel = ProfileViewModel(repository)
    }

    @Test
    fun `ユーザー名が反映された挨拶が返る`() {
        every { repository.fetchUserName() } returns "サトウ"

        val result = viewModel.getGreeting()

        assertEquals("こんにちは、サトウ さん!", result)
        verify { repository.fetchUserName() }
    }
}

MockK アノテーションは、モック化したいプロパティに付与します。
(あとで自動で初期化するため、プロパティはlateinitにしておきます)

ここで重要なのは、MockKでは @MockK を付けただけではモックは自動生成されないという点です。

そのため、MockKAnnotations.init(this) を呼び出して、
@MockK アノテーションが付与されたすべてのプロパティに対して、モックの生成と初期化を行う
必要があります。
これを行わないと、repository は null のままとなり、lateinit property repository has not been initialized という例外が発生します。

また、この初期化処理は @BeforeEach アノテーションを使って定義することで、
各テストごとにクリーンな状態で repository を初期化し、それを使って ProfileViewModel を生成できるようになります。

まとめ

おつかれさまでした。いかがでしたでしょうか!

MockKを使えば、外部依存を切り離して本当にテストしたいロジックに集中できます。
この記事の方法で、ViewModelやUseCase、非同期処理のテストまで幅広く対応できます。

次は、非同期処理(コルーチン)やFlowのモックテストにも挑戦してみましょう〜▼

すだ

技術者としてのキャリアパスを次のレベルへと進めたい皆様、
未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】
を通じて、
自分の市場価値をさらに向上させてみませんか?

それではまた次の記事でお会いしましょう!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

弊社テックブログをご愛読いただきありがとうございます。
当テックブログを運用している株式会社メモリアインクは、
【正社員】還元率83%
【フリーランス】マージン一律5万円で案件のご紹介
と、エンジニアの皆様に分かりやすい形で稼げる仕組みを構築し提供させていただいております。

コメント

コメントする

目次