[Android] 単体テスト用の依存関係をHiltで解決する
目次
プロダクションとは異なる単体テストの依存関係をHiltで解決したい、ということってありますよね。僕はありました。
本記事では、単体テスト用の依存関係をHiltで解決する方法を示していきます。
単体テスト用の依存関係のセットアップ
Hiltのセットアップはできているものとして、さらに単体テスト用の依存関係を追加します。テスト用のHiltの依存関係の他、JUnit 4で使用するテストランナーとRobolectricを使います。
// use Hilt in tests
testImplementation "com.google.dagger:hilt-android-testing:2.46.1"
kaptTest "com.google.dagger:hilt-android-compiler:2.46.1"
// TestRunner
testImplementation "androidx.test.ext:junit-ktx:1.1.5"
// Robolectric
testImplementation "org.robolectric:robolectric:4.10.3"
テストクラスのセットアップ
テストクラスのセットアップにいくつかポイントがありますが、結論としては以下のように行いました。
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import org.junit.Test
import org.robolectric.annotation.Config
import javax.inject.Inject
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(AndroidJUnit4::class)
class ExampleUnitTest {
// 後述
}
ポイントは、以下です。
@HiltAndroidTest
を付与すること- プロダクションで使う
@AndroidEntryPoint
のようなアノテーションと同様に、Hiltが依存関係を解決するために必要です。
- プロダクションで使う
@Config
にHiltTestApplicationを指定すること@Config
自体はRobolectricの仕組みです。@Config
にHiltが定義しているテスト用のApplicationクラスを指定しています。
@RunWith
にAndroidJUnit4を指定すること@RunWith
でテストランナーを指定します。Robolectricとandroidx.testで書いたように、ここで指定するのはRobolectricTestRunnerでも問題ありません。
テストルールの設定
テストルールとしてHiltAndroidRuleを指定します。setupでHiltAndroidRule#injectを呼び出すことでInjectされます。
import dagger.hilt.android.testing.HiltAndroidRule
import org.junit.Before
import org.junit.Rule
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(AndroidJUnit4::class)
class ExampleUnitTest {
@get:Rule
var hiltRule = HiltAndroidRule(this)
@Inject lateinit var animal: Animal
@Before
fun setup() {
hiltRule.inject()
}
}
Moduleの差し替え
test
内にテスト用のモジュールを定義して、@TestInstallIn
でそのモジュールを指定します。これを行うことで、単体テストの依存解決時に使用されるモジュールを差し替えることができます。またここでは、単体テストの依存解決時にMockkを利用したテストダブルを注入するように設定しています。
import dagger.Module
import dagger.Provides
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import io.mockk.every
import io.mockk.mockk
@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [AnimalModule::class]
)
class FakeAnimalModule {
@Provides
fun provideAnimal(): Animal {
return mockk<Animal>().also {
every { it.power() } returns 100
}
}
}
サンプルプロジェクトはGitHubに公開してあります。
書いている人 😎

茨城県つくば市在住のモバイルアプリケーションアーキテクト(Androidが得意です)。モバイルアプリのアーキテクチャ、自動テスト、CI/CDに興味があります。いわゆる「レガシーコード」のリファクタリング・リアーキテクチャが好きです。
👉 もっと詳しく
著書 ✍
Android 依存性注入 ヒッチハイク・ガイド🧳
Androidアプリでの依存性注入(Dependency Injection)に入門するためのガイダンスです。依存性注入の概念やメリットを理解し、Dagger Hiltを用いてAndroidアプリに適用する方法を解説しています。
ソフトウェアデザイン 2023年6月号📚
特集「クリーンアーキテクチャとは何か?」の第5章「モバイルアプリ開発における実践」を執筆しました。
Android クリーンアーキテクチャ ヒッチハイク・ガイド🧳
Androidアプリでのクリーンアーキテクチャに入門するためのガイダンスです。クリーンアーキテクチャの概念を理解し、Androidアプリに適用する方法を解説しています。
Android ユニットテスト ヒッチハイク・ガイド🧳
Androidアプリのユニットテストに入門するためのガイダンスです。初学者が混乱せずにAndroidアプリのユニットテストを書き始めることができる、ということを目的としています。
Android MVVMアーキテクチャ入門🛠
Androidアプリ開発の初学者に向けた、MVVM(Model-View-ViewModel)アーキテクチャの入門書を書きました。初学者の方を確実にネクストレベルに引き上げる技術書です。NextPublishingより出版されています。
関連記事 👀
- GitHub ActionsでSonarCloudにカバレッジをアップロードする
- [Android] Uniflowを用いたMVI的なアーキテクチャを試してみる🦄
- 俺のパブリックリポジトリ
- GitHub ActionsでktlintとAndroid Lintを並列実行して、DangerでPRにまとめてコメントする🐝
- ランチタイムLT会 #1で「何故、UseCaseは1メソッドなのか」というLTをしました