Android Unit Test ViewModel Wanted but not invoked

473 views Asked by At

I'm new on unit testing. I'm trying to do unit testing on my view model class but my test fail with error:

Wanted but not invoked: toggleMovieFavorite.invoke( Movie(id=1, title=Title, overview=Overview, releaseDate=01/01/2025, posterPath=, backdropPath=, originalLanguage=ES, originalTitle=Title, popularity=5.0, voteAverage=7.0, favorite=false) ); -> at xyz.jonthn.usescases.ToggleMovieFavorite.invoke(ToggleMovieFavorite.kt:7) Actually, there were zero interactions with this mock.

This is my test file

@RunWith(MockitoJUnitRunner::class)
class DetailViewModelTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Mock
    lateinit var findMovieById: FindMovieById

    @Mock
    lateinit var toggleMovieFavorite: ToggleMovieFavorite

    @Mock
    lateinit var observer: Observer<Movie>

    private lateinit var vm: DetailViewModel

    @ExperimentalCoroutinesApi
    @Before
    fun setUp() {

        Dispatchers.setMain(Dispatchers.Unconfined)

        vm = DetailViewModel(1, findMovieById, toggleMovieFavorite, Dispatchers.Unconfined)
    }

    @ExperimentalCoroutinesApi
    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `when favorite clicked, the toggleMovieFavorite use case is invoked`() {
        runBlocking {

            val movie = mockedMovie.copy(id = 1)

            whenever(findMovieById.invoke(1)).thenReturn(movie)

            whenever(toggleMovieFavorite.invoke(movie)).thenReturn(movie.copy(favorite = !movie.favorite))

            vm.movie.observeForever(observer)

            vm.onFavoriteClicked()

            verify(toggleMovieFavorite).invoke(movie)
        }
    }

    val mockedMovie = Movie(
    0,
    "Title",
    "Overview",
    "01/01/2025",
    "",
    "",
    "ES",
    "Title",
    5.0,
    7.0,
    false)
}   

This is my DetailViewModel:

class DetailViewModel(
private val movieId: Int, private val findMovieById: FindMovieById,
private val toggleMovieFavorite: ToggleMovieFavorite,
uiDispatcher: CoroutineDispatcher) : ScopedViewModel(uiDispatcher) {

private val _movie = MutableLiveData<Movie>()
val movie: LiveData<Movie> get() = _movie

init {
    launch {
        _movie.value = findMovieById.invoke(movieId)
    }
}

fun onFavoriteClicked() {
    launch {
        movie.value?.let {
            _movie.value = toggleMovieFavorite.invoke(it)
        }
    }
}

}

And my use case ToggleMovieFavorite:

 class ToggleMovieFavorite(private val moviesRepository: MoviesRepository) {
    suspend fun invoke(movie: Movie): Movie = with(movie) {
        copy(favorite = !favorite).also { moviesRepository.update(it) }
    }
    }

Thank you so much for your help guys!!!

1

There are 1 answers

0
iswan iswan On

i thougt mockito does not invoke your init method on viewmodel, you should put your declaration of vm on each @test instead of @Before since method findMovieById called at init, right before the function is mocked.