Angelina
Angelina

Reputation: 1583

Inject fields into ViewModel in tests

How to inject into ViewModel if I write tests. I get error UninitializedPropertyAccessException: lateinit property getAll has not been initialized. I would like to test fetching data from remote API. I am new to writing unit tests so I want to find out how to write it in this case.

class MainViewModel @Inject constructor(
    private val commandProcessor: CommandProcessor,
    private val app: Application
) : AndroidViewModel(app), CoroutineScope {
    var job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    private var notesList: LiveData<List<Note>>? = null
    private var editedNote: LiveData<Note>? = null
    @Inject lateinit var getAll: GetNotes
    @Inject lateinit var add: AddNote

    private fun fetchNotes() {
        getAll.execute()
        notesList = getAll.result
    }

    fun getNotes(): LiveData<List<Note>>? {
        if (notesList == null) fetchNotes()
        return notesList
    }

    override fun onCleared() {
        super.onCleared()
        job.cancel()
        val commands = arrayListOf(getAll, add, delete, getSingle, edit)
        commands.forEach { it.cancelJob() }
    }
}

Test sample:

@RunWith(MockitoJUnitRunner::class)
class MainViewModelTest {
    private lateinit var viewModel: MainViewModel
    val app: Application = mock()
    @Inject lateinit var getAllMock: GetNotes

    @Before
    fun setUp() {
        viewModel = MainViewModel(CommandProcessor(), app)
        Mockito.`when`(viewModel.getAll).thenReturn(getAllMock)
    }

    @Test
    fun testGetNotes() {
        val livedata = MutableLiveData<List<Note>>()
        Mockito.`when`(getAllMock.result).thenReturn(livedata)
        assert(viewModel.getNotes() is LiveData<List<Note>>)
    }
}

Upvotes: 1

Views: 1456

Answers (1)

MyrmidonXIIV
MyrmidonXIIV

Reputation: 72

Because:

  1. lateinit var getAll: GetNotes (in MainViewModel) is a variable (not a method).
  2. anyone else outside MainViewModel can access this variable lateinit var getAll: GetNotes.
  3. this variable is mutable.
  4. MainViewModel instance in setUp() method is not a real mock instance by Mockito.mock(MainViewModel::class.java)

Then: To test logic in MainViewModel, we can create MainViewModel instance and simply set that variable in setUp() method.

private lateinit var mockGetAll: GetNotes

@Before
fun setUp() {
    mockGetAll = mock(GetNotes::class.java)
    viewModel = MainViewModel(CommandProcessor(), app).apply {
        getAll = mockGetAll
    }
}

After this, we can mock any behaviours for our mockGetAll: GetNotes.

Upvotes: 3

Related Questions