https://kotlinlang.org logo
m

Milan Vadhel

09/01/2021, 5:48 AM
Hello I am implementing one sample for unit testing. This is my test class.
Copy code
@SmallTest
@RunWith(MockitoJUnitRunner::class)
@ExperimentalCoroutinesApi
class CharacterViewModelTest {


    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    val testCoroutineRule = TestCoroutineRule()

    @Mock
    lateinit var characterUseCase: CharacterUsecase

    @Mock
    lateinit var characterRepository: CharacterRepository

    @Mock
    lateinit var characterLiveData: Observer<UiState<ArrayList<CharacterItem>>>

    private val characterViewModel: CharacterViewModel by lazy {
        CharacterViewModel(characterUseCase)
    }

    @Test
    fun test_characterApiCallSuccess() {
        stubCharacterApiSuccess()
        characterViewModel.charactersLiveEvent.observeForever(characterLiveData)
        characterViewModel.getCharacters(1)
        verify(characterLiveData).onChanged(UiState.Loading()) // Test Loading Response
        verify(
            characterLiveData
        ).onChanged(UiState.Success(characterApiResponse())) // Test Success Response
        characterViewModel.charactersLiveEvent.removeObserver(characterLiveData)
    }

    private fun stubCharacterApiSuccess() {
        testCoroutineRule.runBlockingTest {
            doReturn(characterApiResponse())
                .`when`(characterRepository)
                .getCharacters(1)
        }
    }
}
I am getting below error..
Wanted but not invoked:
characterLiveData.onChanged(
Success(data=[CharacterItem(name=Rick Sanchez, image=<https://rickandmortyapi.com/api/character/avatar/1.jpeg>, status=Alive, gender=Male, species=Human)])
);
-> at com.example.databindingsample.CharacterViewModelTest.test_characterApiCallSuccess(CharacterViewModelTest.kt:55)
Actually, there were zero interactions with this mock.
I can't figure out what this error is saying. Is there any thing i am doing wrong. Any one have any idea about it?
k

Klaas Kabini

09/01/2021, 6:21 AM
Your unit test is incorrect. You have to verify the behaviour of the collaborator which is the CharacterUseCase not the observer. The observer is not the collaborator. The CharacterUseCase is the interactor(or collaborator) that is used by the view model to achieve a specific use case.
I also noticed that when observing using non-lifecycle bound observers the onChanged does not get called at times and behaves strangely. I had to do this to get it to catch the onChanged events.
Copy code
fun <T> LiveData<T>.attachDisposableObserver(time: Long = 2, timeUnit: TimeUnit = TimeUnit.SECONDS, afterObserve: ()-> Unit) {
   
    val latch = CountDownLatch(1)

    val observer = object : Observer<T> {
        override fun onChanged(t: T?) {
            
            latch.countDown()
            this@gattachDisposableObserver.removeObserver(this)
        }
    }

    this.observeForever(observer)

    try{
    
       // Don't wait indefinitely if the LiveData is not set.
      if (!latch.await(time, timeUnit)) {
        throw TimeoutException("LiveData value was never set.")
      }
    } finally{
        this.removeObserver(observer)
    }

    afterObserve.invoke()
}
15 Views