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!!!
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.