I have a method in my SearchViewModel and I want to test this method by Mockito and JUnit4.(searchCity()) but this error is shown after running the test:
kotlin.UninitializedPropertyAccessException: lateinit property mRepository has not been initialized
SearchViewModel class :
class SearchViewModel @Inject constructor() : BaseViewModel() {
@Inject
lateinit var mRepository: DataRepository
@Inject
lateinit var sharedPfs: SharedPrefs
private var disposable: Disposable? = null
val search = MutableLiveData<ResponseSearch>()
val searchOWM = MutableLiveData<ResponseOWMCity>()
val searchCityName = MutableLiveData<String>()
val serachClick = SingleLiveEvent<Boolean>()
val progressBar = SingleLiveEvent<Boolean>()
val searchOWMvisibility = SingleLiveEvent<Boolean>()
val cityOWMclick = SingleLiveEvent<ResponseOWMCity>()
override fun getSharedPrefs(): SharedPrefs? {
return sharedPfs
}
fun stop() {
disposable?.let { if (!it.isDisposed) it.dispose() }
}
fun fabSearchClick(){
serachClick.call()
}
fun searchCity() {
val cityName = searchCityName.value
if (!Strings.isEmptyOrWhitespace(cityName)) {
progressBar.postValue(true)
disposable = mRepository.doSearchProcess(cityName)
?.subscribe({
search.postValue(it)
progressBar.postValue(false)
}, {
showToast(it!!.message!!)
progressBar.postValue(false)
})
} else{
showToast("لطفا شهر دلخواه خود را وارد کنید.")
}
}
fun searchCityOWM() {
val cityName = searchCityName.value
disposable = mRepository.doSearchProcessOWM(cityName)
?.subscribe({
if (it != null){
searchOWM.postValue(it)
searchOWMvisibility.postValue(true)
} else{
searchOWMvisibility.postValue(false)
}
}, {
searchOWMvisibility.postValue(false)
})
}
fun clickCityOWM(city: ResponseOWMCity){
cityOWMclick.postValue(city)
}
}
DataRepository class :
class DataRepository @Inject
constructor(private val endPointAPI: EndPointAPI, private val localRoomDatabse: LocalRoomDatabse) {
fun getAllSavedResults(): LiveData<List<City?>>? {
return localRoomDatabse.roomDao().getAllResults()
}
fun doSearchProcess(city: String?): Observable<ResponseSearch>? {
return endPointAPI.searchCities(Config.BASE_URL2 + city)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.doOnError({ throwable -> Log.i("1397", "remote: " + throwable.message) })
}
}
SearchViewModelTest :
class SearchViewModelTest {
@get:Rule
val mockitoRule: MockitoRule = MockitoJUnit.rule()
@get:Rule
val taskExecutorRule = InstantTaskExecutorRule()
@Rule
@JvmField
var testSchedulerRule = RxImmediateSchedulerRule()
@Mock
lateinit var observer: Observer<ResponseSearch>
@Mock
lateinit var mRepository: DataRepository
lateinit var searchViewModel: SearchViewModel
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
searchViewModel = SearchViewModel()
}
@Test
fun doSearchResultSuccessWithData() {
// GIVEN
val res = RESULT()
res.name = "shiraz"
val list = ArrayList<RESULT>()
list.add(res)
val search = ResponseSearch(list)
val observable = Observable.just(search)
// WHEN
searchViewModel.searchCityName.value = "shiraz"
searchViewModel.search.observeForever(observer)
whenever(mRepository?.doSearchProcess("shiraz")).thenReturn(observable)
searchViewModel.searchCity()
// THEN
assertNotNull(searchViewModel.search.value)
assertThat(searchViewModel.search.value?.results?.size, CoreMatchers.`is`(1))
}
}
can anyone help me?
While using Dagger and field injection, you should actually inject them with component or factory when it comes to ViewModels. While testing you can inject that mock you created here
@Mock
lateinit var mRepository: DataRepository
using auto-generated code by Dagger
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
searchViewModel = SearchViewModel()
SearchViewModel_MembersInjector.injectMRepository(searchViewModel, mRepository)
}