You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Jetpack Compose导航测试:如何Mock ViewModel工厂?

问题描述

使用Kotlin开发Jetpack Compose应用时,在导航测试中需要Mock ViewModel以脱离API等外部资源依赖,但尝试多种方法无效,不清楚如何Mock ViewModel工厂。

现有代码如下:

ViewModel工厂代码

object AppViewModelProvider {
    val Factory = viewModelFactory {

        initializer {
            FavoriteViewModel(fieldFavoritesApplication().container.favoriteRepository)
        }
    //More initializers...
    }
}

fun CreationExtras.fieldFavoritesApplication(): FieldFavoritesApplication =
    (this[AndroidViewModelFactory.APPLICATION_KEY] as FieldFavoritesApplication)

导航宿主函数

fun FieldFavoritesNavHost(
    navController: NavHostController,
    modifier: Modifier = Modifier,
    favoriteViewModel: FavoriteViewModel = viewModel(factory = AppViewModelProvider.Factory)
)

当前测试代码

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController:TestNavHostController

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            FieldFavoritesApp(navController)
        }
    }

    @Test
    fun verifyStartDestination()  {
        Assert.assertEquals(LeaguesDestination.route, navController.currentBackStackEntry?.destination?.route)
    }
}
解决方案

方案1:直接传入Mock ViewModel(推荐,更简洁)

你的FieldFavoritesNavHost已经将favoriteViewModel作为可配置参数暴露,测试时无需Mock工厂,直接传入Mock实例即可:

  1. 添加MockK依赖(Kotlin项目常用Mock库)
    在模块的build.gradle.kts中添加测试依赖:
testImplementation("io.mockk:mockk:1.13.8")
androidTestImplementation("io.mockk:mockk-android:1.13.8")
  1. 修改测试代码,注入Mock ViewModel
import io.mockk.mockk
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.navigation.testing.TestNavHostController
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController
    // 创建宽松Mock的ViewModel(自动忽略未 stub 的方法调用)
    private val mockFavoriteViewModel: FavoriteViewModel = mockk(relaxed = true)

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            // 直接渲染NavHost并传入Mock的ViewModel,跳过整个App层级更适合导航测试
            FieldFavoritesNavHost(
                navController = navController,
                favoriteViewModel = mockFavoriteViewModel
            )
        }
    }

    @Test
    fun verifyStartDestination() {
        assertEquals(LeaguesDestination.route, navController.currentBackStackEntry?.destination?.route)
    }
}

方案2:Mock ViewModel工厂(适用于多ViewModel需统一Mock场景)

如果需要Mock多个ViewModel,可直接MockAppViewModelProvider.Factory:

  1. 创建Mock工厂并Stub初始化逻辑
import io.mockk.every
import io.mockk.mockk

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController
    private val mockFavoriteViewModel: FavoriteViewModel = mockk(relaxed = true)
    private val mockViewModelFactory: ViewModelProvider.Factory = mockk()

    @Before
    fun setupAppNavHost() {
        // Stub工厂的create方法,当请求FavoriteViewModel时返回Mock实例
        every { 
            mockViewModelFactory.create(FavoriteViewModel::class.java) 
        } returns mockFavoriteViewModel
        // 其他ViewModel的Stub逻辑同理添加

        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            FieldFavoritesNavHost(
                navController = navController,
                favoriteViewModel = viewModel(factory = mockViewModelFactory)
            )
        }
    }

    // ...测试方法
}

关键说明

  • 优先选择方案1,因为直接传入Mock实例更直观,减少不必要的工厂Mock逻辑
  • 使用mockk(relaxed = true)可以避免为ViewModel的每个方法单独编写Stub,适合只关注导航流程的测试
  • 导航测试建议直接渲染FieldFavoritesNavHost而非整个App,减少无关组件的干扰

内容的提问来源于stack exchange,提问作者Milosz_p

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:50:37