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

如何在类型安全Compose导航中测试SavedStateHandle并解决报错?

解决类型安全Compose导航中SavedStateHandle测试的BaseBundle未Mock问题

我正在开发一个使用类型安全Compose导航(androidx.navigation:navigation-compose:2.8.0-beta05)的Android项目,尝试编写测试验证通过SavedStateHandle传递的值。

现有代码

Screen.kt

@Serializable
sealed class Screen {
    @Serializable
    data class Details(
        val movieId: Int,
    ) : Screen()
}

DetailsViewModel.kt

import androidx.navigation.toRoute
import com.paulohc.movlist.navigation.Screen

class DetailsViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
) : ViewModel() {
    val movieId: Int = savedStateHandle.toRoute<Screen.Details>().movieId
}

报错的测试代码

class DetailsViewModelTest {
    private lateinit var detailsViewModel: DetailsViewModel

    @BeforeEach
    fun setup() {
        val savedState = SavedStateHandle(mapOf("movieId" to 10))
        detailsViewModel = DetailsViewModel(savedState)
    }

    @Test
    fun getState() {
        assertThat(detailsViewModel.movieId).isEqualTo(10)
    }
}

报错信息

Method putInt in android.os.BaseBundle not mocked. See https://developer.android.com/r/studio-ui/build/not-mocked for details.
java.lang.RuntimeException: Method putInt in android.os.BaseBundle not mocked. See https://developer.android.com/r/studio-ui/build/not-mocked for details.

解决方案

这个错误源于类型安全导航的toRoute方法内部依赖Android的Bundle类,而单元测试环境默认未Mock这类Android框架类。你可以用MockK模拟SavedStateHandle来解决,具体步骤如下:

  1. 添加MockK依赖到Module级build.gradle:
testImplementation "io.mockk:mockk:1.13.10"
testImplementation "junit:junit:4.13.2"
  1. 修改测试代码,模拟SavedStateHandle并指定toRoute的返回值:
import io.mockk.every
import io.mockk.mockk
import org.junit.Before
import org.junit.Test
import org.junit.Assert.assertEquals

class DetailsViewModelTest {
    private lateinit var detailsViewModel: DetailsViewModel
    private val mockSavedStateHandle = mockk<SavedStateHandle>()

    @Before
    fun setup() {
        every { mockSavedStateHandle.toRoute<Screen.Details>() } returns Screen.Details(movieId = 10)
        detailsViewModel = DetailsViewModel(mockSavedStateHandle)
    }

    @Test
    fun getState() {
        assertEquals(10, detailsViewModel.movieId)
    }
}

替代方案:使用Instrumented测试

如果不想用MockK,可将测试改为Instrumented测试(运行在Android设备/模拟器上):

  • 将测试类移到androidTest目录下
  • 添加@RunWith(AndroidJUnit4::class)注解
  • 原测试代码无需修改即可正常运行,但测试速度会慢于单元测试

核心思路

用MockK模拟的方式直接绕过toRoute内部的Bundle操作,预设路由对象的返回值,从而快速验证ViewModel是否正确获取到参数,适合单元测试场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:13:18