I have a ViewModel. It calls a funtion in my data repo and gets back a list of the dog object.
class MainViewModel() : ViewModel() {
private val dataRepo: DataRepo by inject(DataRepo::class.java) //dataRepo
private var limit = 10
private val _dogListLiveData = MutableLiveData<List<Dog>>()
private var dogList = mutableListOf<Dog>()
val dogListLiveData: MutableLiveData<List<Dog>>
get() = _dogListLiveData
fun searchByBreed(queryText: String) {
dataRepo.searchByBreed(
queryText,
object : DataSource.OnResponseCallback<List<Dog>, String> {
override fun onSuccess(obj: List<Dog>?) {
dogList = mutableListOf()
if(!obj.isNullOrEmpty()){
dogList.addAll(obj)
dogListLiveData.value = dogList.take(limit)
}
}
override fun onError(error: String) {
Log.i("Calling Network Service", error)
}
})
}
fun loadPaginateBreed() : Boolean{
return if ((limit+10) < dogList.size) {
limit += 10
Log.i("Pagination new Limit", limit.toString())
dogListLiveData.value = dogList.take(limit)
false
}else{
limit += dogList.size%limit
dogListLiveData.value = dogList.take(limit)
true
}
}
}
I need to write a simple unit test for it. I've written this and tried many other iterations. But nothing seems to work.
package com.example.koinapplication.ui.main
import androidx.lifecycle.Observer
import com.example.koinapplication.custom.adpaters.GranularErrorCallAdapterFactory
import com.example.koinapplication.models.Dog
import com.example.koinapplication.models.Height
import com.example.koinapplication.models.Weight
import com.example.koinapplication.repo.*
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.Assert.*
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.dsl.module
import org.koin.java.KoinJavaComponent.inject
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import java.util.*
class MainViewModelTest {
private val mainViewModel: MainViewModel by inject(MainViewModel::class.java)
lateinit var obserserData : Observer<List<Dog>>
private val networkModule = module {
factory { AuthInterceptor() }
factory { provideOkHttpClient(get()) }
factory { GranularErrorCallAdapterFactory<Any>() }
single { providesNetworkClient(get(), get()) }
single { DataRepo(get()) }
single { NetworkRepo(get()) }
}
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
startKoin {
modules(arrayListOf(networkModule))
}
}
@After
fun tearDown() {
stopKoin()
}
@Test
fun searchByBreed() {
mainViewModel.dogListLiveData.observeForever { obserserData }
mainViewModel.searchByBreed("dal")
Mockito.verify(obserserData).onChanged(mainViewModel.dogListLiveData.value)
}
}
Please help me write a simple test to test the data in my viewModel. Help will be hugely appreciated.
It is good practice keeping all Dependency Injection (DI) Frameworks out of your ViewModel. This makes your ViewModel independent and simplifies your unit tests.
ViewModel
So instead of injecting your dependency in your ViewModel you can pass them via constructor:
class MainViewModel(
private val dataRepo: dataRepo
) : ViewModel() {
// ...
}
Koin Module
In your Koin module you can define the dependencies and provide an instance of your ViewModel:
module {
single { DataRepo(get()) }
factory { MainViewModel(dataRepo = get())}
}
In your Activity or Fragment you can then inject your ViewModel as usual.
Unit Test
In your MainViewModelTest
you do not need any code for Koin:
class MainViewModelTest {
private val dataRepo: DataRepo = mockk() // I used Mockk for mocking, but you can use any other mocking framework
private val mainViewModel = MainViewModel(dataRepo) // your class under test
@Test
fun yourTest() {
// prepare
every { dataRepo.searchByBreed(...)} returns ...
mainViewModel.searchByBreed(queryText = "...")
// do assertions
}
}
Having this, simplifies writing unit tests a lot. And you could exchange Koin with any other DI Framework later without the need touching your ViewModel and the test(s).
I hope this helps you a bit.