如何在类型安全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来解决,具体步骤如下:
- 添加MockK依赖到Module级
build.gradle:
testImplementation "io.mockk:mockk:1.13.10" testImplementation "junit:junit:4.13.2"
- 修改测试代码,模拟
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
相关产品推荐
相关产品推荐

