1

I want to test a LiveData's value. This LiveData's value depends on another LiveData in the ViewModel. When I run the app, the UI is working as intended. However, when I create a unit test to test the dependent LiveData, the test always fails with an AssertionError saying that the value is always null.

I have the following LiveData in my ViewModel.

class MovieViewModel @ViewModelInject constructor(
    private val movieRepository: MovieRepository
) : ViewModel() {
    private val state = movieRepository.getMoviesState()
    val errorLoading: LiveData<Boolean>
        get() = Transformations.map(state) { it == State.ERROR }
    val isLoading: LiveData<Boolean>
        get() = Transformations.map(state) { it == State.LOADING }
    ...
}

The LiveData I want to test are errorLoading and isLoading. To do that, I use Mockk to mock objects and have the following code to test them both.

    @Test
    fun ensure_isLoading_is_true_when_state_is_loading() {
        ...
        every { movieRepository.getMoviesState() } returns MutableLiveData(State.LOADING)
        every { booleanObserver.onChanged(any()) } just runs
        viewmodel = MovieViewModel(movieRepository)
        verify { movieRepository.getMoviesState() }
        viewmodel.isLoading.observeForever(booleanObserver)
        verify { booleanObserver.onChanged(true) }
        assertEquals(true, viewmodel.isLoading.value)
        viewmodel.isLoading.removeObserver(booleanObserver)
    }

So far, the test can verify that onChanged on the mocked Observer is called and the value of the new change is also correct. However, when I want to access the value of the LiveData I want to test, it always returns null.

I am aware that the LiveData needs to be observed and I have made a mock Observer to do that. However, the test still fails. Why does it always return null?

Richard
  • 7,037
  • 2
  • 23
  • 76

2 Answers2

0

Simply use this :

@Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new 
InstantTaskExecutorRule();
hamid Mahmoodi
  • 690
  • 4
  • 16
0

I had the same issue and discovered that the problem is with backing property. When you use it with get() like this:

val isLoading: LiveData<Boolean>
    get() = Transformations.map(state) { it == State.LOADING }

then when calling isLoading you get a new (empty) LiveData instance. Simply get rid of backing property and use it that way:

val _isLoading = MutableLiveData<Boolean>
val isLoading: LiveData<Boolean> = Transformations.map(state) { it == State.LOADING }
M. Wojcik
  • 2,301
  • 3
  • 23
  • 31