はじめに
皆さん、ごきげんよう!れぶです!
今回の記事では、Compose
× Hilt
× ViewModel
を使用したプロジェクトでUIテストする場合に沼った箇所があったので、その内容と解決方法を記していきます。
UIテスト
でHiltが絡むと意外と厄介だと発見したので、今後上記のようなプロジェクトでUIテストする方には特に参考になるのではないかと思います。
約7分以内で読めます。それでは、参りましょう!!
開発環境
- MacBook Air
- Android Studio Flamingo | 2022.2.1
- Kotlin 1.8.0
- Compose 1.4.2
- compileSdkVersion 33
- minSdkVersion 21
ハマった箇所
下記のようにHiltのUIテストに必要な記述を追加して、testMyComposable()
を開始した場合に事件は起こります。
@HiltAndroidTest
class ComposeTest {
@get:Rule(order = 0)
var hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeTestRule = createComposeRule()
private lateinit var viewModel: MyViewModel
@Before
fun setup() {
hiltRule.inject()
}
@Test
fun testMyComposable() {
composeTestRule.setContent {
viewModel = hiltViewModel()
MyComposable(viewModel)
}
// ...
}
}
以下のエラーが出てきます。通常のCompoeのUIテストのように書いてもうまくいきません。
Hilt test, com.sampleapp.ComposeTest, cannot use a @HiltAndroidApp application .To fix, configure the test to use HiltTestApplication or a custom Hilt test application generated with @CustomTestApplication.
「テストクラスが、HiltAndroidApp
アノテーションが付いた通常のアプリケーションクラスを使用することはできないぞ」と、怒られています。Hiltが絡むテストでは、Hiltが提供する独自のテストアプリケーションクラスを使用する必要があるためです。
根本の原因はCompose側ではなく、Hilt側の問題でした。
解決方法
このエラーを解消するためには、テストクラスがHiltTestApplication
を使用するように設定するか、CustomTestApplicationアノテーションを付けたカスタムテストアプリケーションクラスを作成して使用するかの二択になります。今回は前者で対応します。
以下はエラー解決の3ステップです。
1. HiltTestApplicationが使用するようにCustomTestRunnerを設定
package com.sampleapp
class CustomTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
defaultConfig {
//testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "com.sampleapp.CustomTestRunner"
}
通常テストランナーはJUnit
によってテストケースを実行する方法を決定します。
しかし今回はHiltTestApplication
を使用したいので、HiltTestApplicationインスタンスを生成するテストランナーを独自に作成し、build.gradleに指定します。
2. AndroidEntryPointアノテーションが付いたテスト用のActivityをdebugフォルダに新規作成
@AndroidEntryPoint
class HiltTestActivity : ComponentActivity()
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sampleapp">
<application>
<activity
android:name=".HiltTestActivity"
android:exported="false" />
</application>
</manifest>
いわゆる、ダミーActivityです。Manifestへの登録も忘れずに。
3. createAndroidComposeRule()に、上記で作ったActivityを指定
@HiltAndroidTest
class ComposeTest {
@get:Rule(order = 0)
var hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<HiltTestActivity>()
private lateinit var viewModel: MyViewModel
@Before
fun setup() {
hiltRule.inject()
}
@Test
fun testMyComposable() {
composeTestRule.setContent {
viewModel = hiltViewModel()
MyComposable(viewModel)
}
// ...
}
}
今回のようにActivityに直接アクセスし、そこへComposable関数を起動させる場合には、createAndroidComposeRule
を使用します。
おわりに
今回はCompose
× Hilt
× ViewModel
を使用したプロジェクトでUIテストする場合に発生したエラーに対する対応を記述しました。
自分でカスタムランナーやダミーActivityを作って、設定し直す作業は「とてもめんどいな」と感じた方も少なくないと思います。自分もその1人です。しかし、UIテストにおける引き出しが更に増えたとポジティブに捉えて対応していきましょう。何せ実装面では、HiltによってDIが自動化できるメリットを享受できていますから。
以上です。同じエラーで困ってる方にこの記事が少しでも役立つと嬉しいです。ありがとうございました!