LoginSignup
2
2

【Android】Compose × Hilt × ViewModelのUIテストで発生したエラー:一体何が起こった?

Posted at

はじめに

皆さん、ごきげんよう!れぶです!

今回の記事では、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()を開始した場合に事件は起こります。

ComposeTest.kt
@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を設定

CustomTestRunner.kt
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)
    }
}
build.gradle(app)
defaultConfig {
        //testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        testInstrumentationRunner "com.sampleapp.CustomTestRunner"
}

通常テストランナーはJUnitによってテストケースを実行する方法を決定します。

しかし今回はHiltTestApplicationを使用したいので、HiltTestApplicationインスタンスを生成するテストランナーを独自に作成し、build.gradleに指定します。

2. AndroidEntryPointアノテーションが付いたテスト用のActivityをdebugフォルダに新規作成

HiltTestActivity.kt
@AndroidEntryPoint
class HiltTestActivity : ComponentActivity()
AndroidManifest.xml
<?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を指定

ComposeTest.kt
@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が自動化できるメリットを享受できていますから。

以上です。同じエラーで困ってる方にこの記事が少しでも役立つと嬉しいです。ありがとうございました!

参考サイト

2
2
0

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
2
2