Jetpack 撰写导航 UI 测试

问题描述 投票:0回答:1

我正在 Jetpack Compose 中进行导航测试,并且在我的项目中为 DI 实现了 Hilt。但是当我运行 UI 测试来检查导航时,我遇到了这个异常

给定的组件持有者类 androidx.activity.ComponentActivity 未实现接口 dagger.hilt.internal.GenerateComponent 或接口 dagger.hilt.internal.GenerateComponentManager

这就是我到目前为止所做的:

我的 Gradle 文件看起来像这样

buildscript {
    dependencies {
        classpath("com.google.dagger:hilt-android-gradle-plugin:2.48")
      }
}
plugins {
    id ("com.android.application") version "8.1.1" apply false
    id ("com.android.library") version "8.1.1" apply false
    id ("org.jetbrains.kotlin.android") version "1.8.21" apply false
    id ("com.google.dagger.hilt.android") version "2.48" apply false
    id("com.google.devtools.ksp") version "1.8.10-1.0.9" apply false
}
tasks.register("clean",Delete::class){
    delete(rootProject.buildDir)
}
plugins {
    id ("com.android.application")
    kotlin("android")
    kotlin("kapt")
    id ("dagger.hilt.android.plugin")
}

android {
    namespace = "com.example.boundarys"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.boundarys"
        minSdk = 26
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "com.example.boundarys.HiltTestRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_18
        targetCompatibility = JavaVersion.VERSION_18
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_18.toString()
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.7"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {
    coreLibraryDesugaring (libs.desugar.jdk.libs)
    implementation(project(":KtorModule"))
    implementation(project(":BxExtension"))

    implementation (libs.core.ktx)
    implementation (libs.lifecycle.runtime.ktx)
    implementation (libs.activity.compose)
    implementation (libs.ui)
    implementation (libs.ui.tooling.preview)
    implementation (libs.foundation)
    implementation(libs.material3)
    implementation("androidx.compose.material3:material3-window-size-class:1.2.0-alpha06")
    implementation(libs.androidx.material)

    // testing
    testImplementation (libs.junit)
    androidTestImplementation (libs.androidx.junit)
    androidTestImplementation (libs.androidx.espresso.core)
    androidTestImplementation (libs.androidx.ui.test.junit4)
    debugImplementation (libs.androidx.ui.tooling)
    debugImplementation (libs.androidx.ui.test.manifest)
    testImplementation (libs.truth)
    androidTestImplementation (libs.truth)
    androidTestImplementation (libs.androidx.core.testing)
    testImplementation(libs.kotlinx.coroutines.test)
    androidTestImplementation(libs.kotlinx.coroutines.test)
    testImplementation(libs.androidx.core.testing)
    androidTestImplementation(libs.mockito.core)
    androidTestImplementation(libs.mockito.kotlin)
    androidTestImplementation(libs.androidx.navigation.testing)

    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))

    // Compose dependencies
    implementation (libs.androidx.lifecycle.viewmodel.compose)
    implementation (libs.androidx.navigation.compose)
    implementation ("androidx.compose.material:material-icons-extended")



    // Dependency Injection
    implementation(libs.hilt.android)
    kapt(libs.hilt.android.compiler)
    implementation(libs.androidx.hilt.work)
    kapt(libs.androidx.hilt.compiler)
    implementation(libs.androidx.work.runtime.ktx)
    implementation(libs.hilt.navigation.compose)

    // For Robolectric tests.
    testImplementation (libs.hilt.android.testing)
    // ...with Kotlin.
    kaptTest (libs.hilt.android.compiler)
    // ...with Java.
    testAnnotationProcessor (libs.hilt.android.compiler)


    // For instrumented tests.
    androidTestImplementation (libs.hilt.android.testing)
    // ...with Kotlin.
    kaptAndroidTest (libs.hilt.android.compiler)
    // ...with Java.
    androidTestAnnotationProcessor (libs.hilt.android.compiler)

    // Lifecycle
    implementation(libs.androidx.lifecycle.viewmodel.compose)
    implementation(libs.androidx.lifecycle.viewmodel.ktx)
    implementation(libs.androidx.lifecycle.runtime.compose)

    // Room
    implementation(libs.androidx.room.runtime)
    implementation(libs.androidx.room.ktx)
    implementation(libs.androidx.runtime.livedata)
    kapt(libs.androidx.room.compiler)
    annotationProcessor( libs.androidx.room.compiler)

    // constraint layout
    implementation (libs.androidx.constraintlayout.compose)

    // Glide
    implementation (libs.landscapist.glide)

    // GSON
    api(libs.gson)
}

我有这个 HiltTestRunner 文件并添加到 gradle 你可以看到

testInstrumentationRunner = "com.example.boundarys.HiltTestRunner"

文件看起来像这样

class HiltTestRunner : AndroidJUnitRunner(){
    override fun newApplication(
        cl: ClassLoader?,
        className: String?,
        context: Context?
    ): Application {
        return super.newApplication(cl, HiltTestApplication::class.java.name, context)

    }
}

我的Activity是这样的,也在Manifest中定义了应用程序文件

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        init()
        setContent {
            BoundarysTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    BoundarysNavGraph()
                }
            }
        }
    }
}
@HiltAndroidApp
class BoundarysApp : Application(){
}

我使用嵌套图来实现

@Composable
fun BoundarysNavGraph(navController: NavHostController = rememberNavController()){
    NavHost(
        navController = navController,
        startDestination =BoundarysNavRoutes.Splash.route){
        navigation(
            startDestination =SplashScreenRoutes.SPLASH.route ,
            route=BoundarysNavRoutes.Splash.route,
        ){
            splashNavGraph(navController)
        }
    }
}


fun NavGraphBuilder.splashNavGraph(navController: NavHostController){
    horizontallyAnimatedComposableEnterOnly(SplashScreenRoutes.SPLASH.route){
        val viewModel = hiltViewModel<SplashViewModel>()
        SplashScreen(navController)
    }
    horizontallyAnimatedComposable(SplashScreenRoutes.LANDING.route){
        val viewModel = hiltViewModel<LandingPageViewModel>()
        LandingPageScreen(navController,viewModel.eventFlow){
            viewModel.onEventUpdate(it)
        }
    }
    horizontallyAnimatedComposable(SplashScreenRoutes.EXPLAINER.route){
        val viewModel = hiltViewModel<ExplainerScreenViewModel>()
        ExplainerScreen(navController,viewModel.eventFlow){
            viewModel.onEventUpdate(it)
        }
    }
}

我的测试文件看起来像这样

@HiltAndroidTest
class SplashNavTest {

    @get:Rule
    val composeTestRule =  createComposeRule()
    @get:Rule
    var hiltRule = HiltAndroidRule(this)
    private lateinit var context: Context

    private lateinit var navController: TestNavHostController

    @Before
    fun init(){
        hiltRule.inject()
        context = ApplicationProvider.getApplicationContext<Context>()
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            BoundarysNavGraph(navController = navController)
        }
    }

    @Test
    fun Verify_SplashIsVisible(){
        val currentDestination = navController.currentBackStackEntry?.destination?.route

        Truth.assertThat(currentDestination).isEqualTo(SplashScreenRoutes.SPLASH.route)
    }

}
android kotlin unit-testing dagger-hilt android-jetpack-navigation
1个回答
0
投票

代码中的第一个问题在于您not指定了

composeTestRule
hiltRule
的顺序。除此之外,为了能够访问相应的活动,应使用
composeTestRule
创建
createAndroidComposeRule
。所以你的代码应该更改如下:

@get:Rule(order = 0) //👈
var hiltRule = HiltAndroidRule(this)

@get:Rule(order = 1) //👈
val composeTestRule = createAndroidComposeRule<MainActivity>() //👈

现在,设置内容时,记得指定活动:

composeTestRule.activity.setContent { ... }
//                👆
© www.soinside.com 2019 - 2024. All rights reserved.