Status Update
Comments
cl...@google.com <cl...@google.com>
qu...@dice.fm <qu...@dice.fm> #2
ty...@gmail.com <ty...@gmail.com> #3
Hoping for an update, just migrated over to type safe navigation and its great but having to convert all of our view model tests to instrumented tests is rough. If this can't be resolved is there any other solutions that don't require going to instrumented tests?
fr...@gmail.com <fr...@gmail.com> #4
I use backStackEntry.toRoute<T>() for now to get the destination and then pass the arguments to the viewModel via DI.
@Serializable data class Home(val argument1: String, val argument2: String)
@Composable fun HomeScreen( destination: Home, viewModel: HomeViewModel = koinViewModel { parametersOf(destination.argument1, destination.argument2) } ) { // content() }
ka...@gmail.com <ka...@gmail.com> #5
Temporary solution can be mocking the actual extension, this way it will never get to the moment of using bundle
@Before
fun before() {
mockkStatic("androidx.navigation.SavedStateHandleKt")
every { savedStateHandle.toRoute<SomeArgument>() } returns SomeArgument("some value")
}
@After
fun after() {
unmockkStatic("androidx.navigation.SavedStateHandleKt")
}
ty...@gmail.com <ty...@gmail.com> #6
This works great, thank you!
hu...@verizonconnect.com <hu...@verizonconnect.com> #7
Based on the previous temporary solution suggested here I created this Rule:
class SavedStateHandleRule(
private val route: Any,
) : TestWatcher() {
val savedStateHandleMock: SavedStateHandle = mockk()
override fun starting(description: Description?) {
mockkStatic("androidx.navigation.SavedStateHandleKt")
every { savedStateHandleMock.internalToRoute<Any>(any(), any()) } returns route
super.starting(description)
}
override fun finished(description: Description?) {
unmockkStatic("androidx.navigation.SavedStateHandleKt")
super.finished(description)
}
}
Usage
class ViewModelTest {
private val route = Route("foo")
@get:Rule
val savedStateHandleRule = SavedStateHandleRule(route)
private val viewModel: ViewModel get() = ViewModel(savedStateHandleRule.savedStateHandleMock)
gi...@gmail.com <gi...@gmail.com> #8
ti...@infraspeak.com <ti...@infraspeak.com> #9
cl...@google.com <cl...@google.com> #10
This issue is due to the need to call NavType.put
and NavType.get
internally when handling SavedState. When SavedState was commonized recently in 2.9.0, its Android implementation was delegated to Bundle. So in the end, SavedState in android unit test is still dealing with Bundle. This means there is still no viable solution to completely Android dependency.
However, this does mean that we can make the SavedStateHandle
test api common-friendly.
Description
Component used: Navigation Version used: 2.8.0-beta04 Devices/Android versions reproduced on: All
Navigation arguments are passed to a more details ).
ViewModel
usingSavedStateHandle
(These have traditionally been accessed using a key which returns a String e.g.
savedStateHandle['myId']
. With the new type-safe APIs, however, they are accessed usingsavedStateHandle.toRoute<MyRoute>().myId
which provides type safety.Use of
toRoute
, however, introduces an Android dependency onandroid.os.Bundle
. This means that any tests forViewModel
s which usetoRoute
need to be converted from unit tests to instrumented tests (e.g. using Robolectric) in order to pass.Instrumented tests increase test time and reduce test fidelity. It is preferable for the new type safe APIs to have no Android dependencies.
Steps to repro:
git clone https://github.com/android/nowinandroid/tree/dt/nav-safe-args-android-dependency && cd nowinandroid
./gradlew :feature:topic:testDemoDebugUnitTest --tests "com.google.samples.apps.nowinandroid.feature.topic.TopicViewModelTest" -i
Expected result:
Actual result: