Search code examples
androidkotlinandroid-jetpack-composeandroid-viewmodel

Mocking ViewModels for ui testing in Android


I'm currently building a jetpack compose app with Kotlin and I experience difficulties with testing the navigation of the app.

I want to mock my ViewModels so my tests are not dependend of the api and so on. I have tried different things for mocking the viewmodels but none of them worked. I have found out that the way to go is by mocking the factory, but I can't figure out how to do it.

For the initialization of the vm's I have the following factory:

object AppViewModelProvider {
    val Factory = viewModelFactory {

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

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

Here is the function signature of the navigation host:

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

And finally the test itself:

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)
    }
}

How can I mock the factory so I can use fake viewmodels in this test?


Solution

  • To mock a ViewModel in a project structured similarly to Inventory app you can create ViewModel file with the same file name and location in the androidTest source set. For example, if you have FavoriteViewModel in ui.favorites package in your app, create ui.favorites package in androidTest and put mock FavoriteViewModel implementation there, it will be used instead of the original one.

    Alternatively, you can keep the original ViewModels but mock repositories instead in a similar way. For example, if you want to mock ItemsRepository in Inventory app, create TestItemsRepository implementing ItemsRepository in the androidTest. Then create AppContainer in data package that will provide your test implementation:

    override val itemsRepository: ItemsRepository by lazy {
        TestItemsRepository()
    }