So, I have this ViewModel class
#HiltViewModel
class HomeViewModel #Inject constructor(private val starWarsRepository: StarWarsRepository) : ViewModel() {
/**
* Two-Way binding variable
*/
val searchQuery: MutableLiveData<String> = MutableLiveData()
val characters = searchQuery.switchMap { query ->
if (query.isNotBlank() && query.isNotEmpty()) {
characterPager.liveData.cachedIn(viewModelScope)
} else {
MutableLiveData()
}
}
fun retrySearch() {
searchQuery.postValue(searchQuery.value)
}
private val characterPager by lazy {
val searchPagingConfig = PagingConfig(pageSize = 20, maxSize = 100, enablePlaceholders = false)
Pager(config = searchPagingConfig) {
CharacterSearchPagingSource(starWarsRepository, searchQuery.value ?: String())
}
}
}
and here it's counter TestingClass
#ExperimentalCoroutinesApi
#RunWith(JUnit4::class)
class HomeViewModelTest {
#get:Rule
val instantTaskExecutionRule = InstantTaskExecutorRule()
private lateinit var homeViewModel: HomeViewModel
private val starWarsAPI = mock(StarWarsAPI::class.java)
private val testDispatcher = UnconfinedTestDispatcher()
#Before
fun setup() {
Dispatchers.setMain(testDispatcher)
homeViewModel = HomeViewModel(StarWarsRepositoryImpl(starWarsAPI, testDispatcher))
}
#AfterTest
fun tearDown() {
Dispatchers.resetMain()
}
#Test
fun `live data should emit success with result when searching for a character`() = runTest {
val character = CharacterEntity("", "", "", "172", "", listOf(), listOf())
`when`(starWarsAPI.searchCharacters("a", null)).thenReturn(CharacterSearchResultEntity(listOf(character, character), null, null))
homeViewModel.searchQuery.value = "a"
val result = homeViewModel.characters.getOrAwaitValue()
assertEquals(PagingSource.LoadResult.Page(listOf(), null, null), result)
}
}
Here is the repository
class StarWarsRepositoryImpl(private val starWarsAPI: StarWarsAPI, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO) : StarWarsRepository {
override suspend fun searchCharacters(query: String, page: Int?) = withContext(coroutineDispatcher) {
starWarsAPI.searchCharacters(query, page)
}
}
I'm using this extension function from a google sample.
fun <T> LiveData<T>.getOrAwaitValue(time: Long = 2, timeUnit: TimeUnit = TimeUnit.SECONDS, afterObserve: () -> Unit = {}): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this#getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
#Suppress("UNCHECKED_CAST")
return data as T
}
The problem is I'm getting an exception from the getOrAwaitValue that LiveData value was never set.?
Related
I used Paging 3 for my Recycler View that had to show some stories in the application. But while I used Paging 3, the lists did not show the item. Before I used Paging 3, the items had been showing including the details.
StoriesResponseItem.kt
#Entity(tableName = "story")
data class StoriesResponseItem(
#PrimaryKey
#field:SerializedName("id")
val id: String,
#field:SerializedName("photoUrl")
val photoUrl: String,
#field:SerializedName("createdAt")
val createdAt: String,
#field:SerializedName("name")
val name: String,
#field:SerializedName("description")
val description: String,
#field:SerializedName("lon")
val lon: Double? = null,
#field:SerializedName("lat")
val lat: Double? = null
)
ApiService.kt
interface ApiService {
#GET("stories")
suspend fun getPagingStories(
#Header("Authorization") Authorization: String,
#Query("page") page: Int,
#Query("size") size: Int
): List<StoriesResponseItem>
}
StoriesPagingSource.kt
class StoriesPagingSource(private val token: String, private val apiService: ApiService) : PagingSource<Int, StoriesResponseItem>() {
private companion object {
const val INITIAL_PAGE_INDEX = 1
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, StoriesResponseItem> {
return try {
val position = params.key ?: INITIAL_PAGE_INDEX
val responseData = apiService.getPagingStories(token, position, params.loadSize)
LoadResult.Page(
data = responseData,
prevKey = if (position == INITIAL_PAGE_INDEX) null else position - 1,
nextKey = if (responseData.isEmpty()) null else position + 1
)
} catch (retryableError: Exception) {
LoadResult.Error(retryableError)
}
}
override fun getRefreshKey(state: PagingState<Int, StoriesResponseItem>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
StoriesRepository.kt
class StoriesRepository(private val apiService: ApiService) {
fun getStories(token: String): LiveData<PagingData<StoriesResponseItem>> {
return Pager(
config = PagingConfig(
pageSize = 5
),
pagingSourceFactory = {
StoriesPagingSource(token, apiService)
}
).liveData
}
}
MainViewModel.kt
class MainViewModel(private val storiesRepository: StoriesRepository) : ViewModel() {
private val _stories = MutableLiveData<PagingData<StoriesResponseItem>>()
fun stories(token: String): LiveData<PagingData<StoriesResponseItem>> {
val response = storiesRepository.getStories(token).cachedIn(viewModelScope)
_stories.value = response.value
return response
}
}
class ViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return MainViewModel(Injection.provideRepository(context)) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val mainViewModel: MainViewModel by viewModels {
ViewModelFactory(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (applicationContext.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
val layoutManager = GridLayoutManager(this, 2)
binding.rvStories.layoutManager = layoutManager
} else {
val layoutManager = LinearLayoutManager(this)
binding.rvStories.layoutManager = layoutManager
}
val pref = AppDataStore.getInstance(dataStore)
val authViewModel = ViewModelProvider(this, ViewModelFactory(pref))[AuthViewModel::class.java]
authViewModel.loginToken().observe(this) { token: String? ->
val loginToken = "Bearer $token"
getData(loginToken)
}
}
private fun getData(loginToken: String) {
val adapter = ListStoriesAdapter()
binding.rvStories.adapter = adapter.withLoadStateFooter(
footer = LoadingStateAdapter {
adapter.retry()
}
)
lifecycleScope.launch {
mainViewModel.stories(loginToken).observe(this#MainActivity) {
adapter.submitData(lifecycle, it)
}
}
}
}
This is my repository for this project :
https://github.com/daffakurnia11/StoryApp
I following the new google guide and end with this to represent the state in viewmodel like tis
var uiState: AccountSettingUiState by
mutableStateOf(AccountSettingUiState.Initial)
private set
then I have this function
fun resetPasswordUseCase(context: Context) {
resetPasswordUseCase.execute(context)
.subscribeOn(rxSchedulers.io)
.doOnSubscribe {
uiState = AccountSettingUiState.Loading
}
.observeOn(rxSchedulers.ui)
.subscribe {
uiState = AccountSettingUiState.Result
}
}
I want to test this function by assert emitting loading then result but how
I can capture the values
I can test only final state
ViewModel
#HiltViewModel
class SearchViewModel #Inject constructor(
private val searchItemUseCase: SearchItemUseCase,
private val searchUIMapper: SearchUIMapper
) : ViewModel() {
var search by mutableStateOf<StateUI<SearchUI>>(StateUI.Init())
fun searchItem(query: String) {
viewModelScope.launch {
search = StateUI.Loading()
searchItemUseCase.execute(query)
.catch { error ->
search = StateUI.Error(error)
}
.map {
searchUIMapper.map(it)
}.collect {
search = if (it.results.isEmpty()) {
StateUI.Error(EmptySearchException())
} else {
StateUI.Success(it)
}
}
}
}
}
Test
#ExperimentalCoroutinesApi
#ExperimentalTime
class SearchViewModelTest {
private lateinit var searchViewModel: SearchViewModel
#get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
#get:Rule
var coroutineRule = TestCoroutineRule()
#MockK(relaxed = true)
private lateinit var searchItemUseCase: SearchItemUseCase
private val searchUIMapper by lazy {
SearchUIMapper(ItemUIMapper())
}
#Before
fun setup() {
MockKAnnotations.init(this)
searchViewModel = SearchViewModel(
searchItemUseCase,
searchUIMapper
)
}
#Test
fun success() = coroutineRule.runBlockingTest {
val search = getSearchEntity(itemEntityList)
//Given
coEvery { searchItemUseCase.execute("") } returns flow {
emit(search)
}
searchViewModel.searchItem("")
assertEquals(StateUI.Success(searchUIMapper.map(search)), searchViewModel.search)
}
#Test
fun emptySearch() = coroutineRule.runBlockingTest {
val search = getSearchEntity(emptyList())
//Given
coEvery { searchItemUseCase.execute("") } returns flow {
emit(search)
}
//When
searchViewModel.searchItem("")
//Verify
assertTrue((searchViewModel.search as StateUI.Error<SearchUI>).data is EmptySearchException)
}
#Test
fun error() = coroutineRule.runBlockingTest {
val error = NoConnectivityException()
//Given
coEvery { searchItemUseCase.execute("") } returns flow {
throw error
}
//When
searchViewModel.searchItem("")
assertEquals(StateUI.Error<SearchUI>(error), searchViewModel.search)
}
}
I am almost new to android testing and following the official docs and Udacity course for learning purposes.
Coming to the issue I want to check when the task is completed or incompleted to be displayed properly or not, for this I wrote a few tests. Here I got the exception that toast can not be displayed on a thread that has not called Looper.prepare.
When I comment out the toast msg live data updating line of code then all tests work fine and pass successfully. I am new to android testing and searched out a lot but did not get any info to solve this issue. Any help would be much appreciated. A little bit of explanation will be much more helpful if provided.
Below is my test class source code along with ViewModel, FakeRepository, and fragment source code.
Test Class.
#ExperimentalCoroutinesApi
#MediumTest
#RunWith(AndroidJUnit4::class)
class TaskDetailFragmentTest {
#get:Rule
var mainCoroutineRule = MainCoroutineRule()
#get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var tasksRepository: FakeTasksRepository
#Before
fun setUp() {
tasksRepository = FakeTasksRepository()
ServiceLocator.taskRepositories = tasksRepository
}
#Test
fun addNewTask_addNewTaskToDatabase() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "1", userId = 0, title = "Hello AndroidX World",false)
tasksRepository.addTasks(newTask)
val task = tasksRepository.getTask(newTask.id)
assertEquals(newTask.id,(task as Result.Success).data.id)
}
#Test
fun activeTaskDetails_DisplayedInUi() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "2", userId = 0, title = "Hello AndroidX World",false)
tasksRepository.addTasks(newTask)
val bundle = TaskDetailFragmentArgs(newTask.id).toBundle()
launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.Theme_ToDoWithTDD)
onView(withId(R.id.title_text)).check(matches(isDisplayed()))
onView(withId(R.id.title_text)).check(matches(withText("Hello AndroidX World")))
onView(withId(R.id.complete_checkbox)).check(matches(isDisplayed()))
onView(withId(R.id.complete_checkbox)).check(matches(isNotChecked()))
}
#Test
fun completedTaskDetails_DisplayedInUI() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "2", userId = 0, title = "Hello AndroidX World",true)
tasksRepository.addTasks(newTask)
val bundle = TaskDetailFragmentArgs(newTask.id).toBundle()
launchFragmentInContainer <TaskDetailFragment>(bundle,R.style.Theme_ToDoWithTDD)
onView(withId(R.id.title_text)).check(matches(isDisplayed()))
onView(withId(R.id.title_text)).check(matches(withText("Hello AndroidX World")))
onView(withId(R.id.complete_checkbox)).check(matches(isDisplayed()))
onView(withId(R.id.complete_checkbox)).check(matches(isChecked()))
}
#After
fun tearDown() = mainCoroutineRule.runBlockingTest {
ServiceLocator.resetRepository()
}
}
FakeRepository class.
class FakeTasksRepository: TasksRepository {
var tasksServiceData: LinkedHashMap<String,Task> = LinkedHashMap()
private val observableTasks: MutableLiveData<Result<List<Task>>> = MutableLiveData()
private var shouldReturnError: Boolean = false
fun setReturnError(value: Boolean) {
shouldReturnError = value
}
override fun observeTasks(): LiveData<Result<List<Task>>> {
return observableTasks
}
override fun observeTask(taskId: String): LiveData<Result<Task>> {
runBlocking { fetchAllToDoTasks() }
return observableTasks.map { tasks ->
when(tasks) {
is Result.Loading -> Result.Loading
is Result.Error -> Result.Error(tasks.exception)
is Result.Success -> {
val task = tasks.data.firstOrNull() { it.id == taskId }
?: return#map Result.Error(Exception("Not found"))
Result.Success(task)
}
}
}
}
override suspend fun completeTask(id: String) {
tasksServiceData[id]?.completed = true
}
override suspend fun completeTask(task: Task) {
val compTask = task.copy(completed = true)
tasksServiceData[task.id] = compTask
fetchAllToDoTasks()
}
override suspend fun activateTask(id: String) {
tasksServiceData[id]?.completed = false
}
override suspend fun activateTask(task: Task) {
val activeTask = task.copy(completed = false)
tasksServiceData[task.id] = activeTask
fetchAllToDoTasks()
}
override suspend fun getTask(taskId: String): Result<Task> {
if (shouldReturnError) return Result.Error(Exception("Test Exception"))
tasksServiceData[taskId]?.let {
return Result.Success(it)
}
return Result.Error(Exception("Could not find task"))
}
override suspend fun getTasks(): Result<List<Task>> {
return Result.Success(tasksServiceData.values.toList())
}
override suspend fun saveTask(task: Task) {
tasksServiceData[task.id] = task
}
override suspend fun clearAllCompletedTasks() {
tasksServiceData = tasksServiceData.filterValues {
!it.completed
} as LinkedHashMap<String, Task>
}
override suspend fun deleteAllTasks() {
tasksServiceData.clear()
fetchAllToDoTasks()
}
override suspend fun deleteTask(taskId: String) {
tasksServiceData.remove(taskId)
fetchAllToDoTasks()
}
override suspend fun fetchAllToDoTasks(): Result<List<Task>> {
if(shouldReturnError) {
return Result.Error(Exception("Could not find task"))
}
val tasks = Result.Success(tasksServiceData.values.toList())
observableTasks.value = tasks
return tasks
}
override suspend fun updateLocalDataStore(list: List<Task>) {
TODO("Not yet implemented")
}
fun addTasks(vararg tasks: Task) {
tasks.forEach {
tasksServiceData[it.id] = it
}
runBlocking {
fetchAllToDoTasks()
}
}
}
Fragment class.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.loadTaskById(args.taskId)
setUpToast(this,viewModel.toastText)
viewModel.editTaskEvent.observe(viewLifecycleOwner, {
it?.let {
val action = TaskDetailFragmentDirections
.actionTaskDetailFragmentToAddEditFragment(
args.taskId,
resources.getString(R.string.edit_task)
)
findNavController().navigate(action)
}
})
binding.editTaskFab.setOnClickListener {
viewModel.editTask()
}
}
ViewModel class.
class TaskDetailViewModel(
private val tasksRepository: TasksRepository
) : ViewModel() {
private val TAG = "TaskDetailViewModel"
private val _taskId: MutableLiveData<String> = MutableLiveData()
private val _task = _taskId.switchMap {
tasksRepository.observeTask(it).map { res ->
Log.d("Test","res with value ${res.toString()}")
isolateTask(res)
}
}
val task: LiveData<Task?> = _task
private val _toastText = MutableLiveData<Int?>()
val toastText: LiveData<Int?> = _toastText
private val _dataLoading = MutableLiveData<Boolean>()
val dataLoading: LiveData<Boolean> = _dataLoading
private val _editTaskEvent = MutableLiveData<Unit?>(null)
val editTaskEvent: LiveData<Unit?> = _editTaskEvent
fun loadTaskById(taskId: String) {
if(dataLoading.value == true || _taskId.value == taskId) return
_taskId.value = taskId
Log.d("Test","loading task with id $taskId")
}
fun editTask(){
_editTaskEvent.value = Unit
}
fun setCompleted(completed: Boolean) = viewModelScope.launch {
val task = _task.value ?: return#launch
if(completed) {
tasksRepository.completeTask(task.id)
_toastText.value = R.string.task_marked_complete
}
else {
tasksRepository.activateTask(task.id)
_toastText.value = R.string.task_marked_active
}
}
private fun isolateTask(result: Result<Task?>): Task? {
return if(result is Result.Success) {
result.data
} else {
_toastText.value = R.string.loading_tasks_error
null
}
}
#Suppress("UNCHECKED_CAST")
class TasksDetailViewModelFactory(
private val tasksRepository: TasksRepository
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return (TaskDetailViewModel(
tasksRepository
) as T)
}
}
}
In this method in ViewModel when I comment out the below line of code all tests passed.
_toastText.value = R.string.loading_tasks_error
private fun isolateTask(result: Result<Task?>): Task? {
return if(result is Result.Success) {
result.data
} else {
_toastText.value = R.string.loading_tasks_error // Comment out this line then all test passed.
null
}
}
I want to unit test my viewmodel and repository but I don't know how I can achieve that. I have made a start wit the viewmodeltestclass but I don't know how I can go further and what the best approach is. Should I also test my endpoint class and mainactivity? can someone help me please?
This is my endpoint class:
interface VenuesEndpoint {
#GET("v2/venues/search")
suspend fun get(
#Query("near") city: String,
#Query("limit") limit: String = Constants.LIMIT,
#Query("radius") radius: String = Constants.RADIUS,
#Query("client_id") id: String = Constants.CLIENT_ID,
#Query("client_secret") secret: String = Constants.CLIENT_SECRET,
#Query("v") date: String
): VenuesMainResponse
}
My repository class:
private val _data: MutableLiveData<VenuesMainResponse?> = MutableLiveData(null)
val data: LiveData<VenuesMainResponse?> get() = _data
suspend fun fetch(city: String, date: String) {
val retrofit = ApiClient()
val api = retrofit.retro.create(VenuesEndpoint::class.java)
try {
val response = api.get(
city = city,
date = date
)
_data.value = response
} catch (e: Exception) {
Log.d(TAG, e.message.toString())
_data.value = null
}
}
}
My viewmodel class:
class VenueViewModel() : ViewModel() {
private val repository = VenuesRepository()
val data: LiveData<VenuesMainResponse?> = repository.data
fun getData(city: String, date: String) {
viewModelScope.launch {
repository.fetch(city, date)
}
}
}
My mainActivity class:
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<VenueViewModel>()
private lateinit var adapter: HomeAdapter
private var searchData: List<Venue>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText = findViewById<EditText>(R.id.main_search)
viewModel.getData(
city = Constants.CITY,
date = Constants.DATE
)
viewModel.data
.observe(this, Observer {
it.let {
initAdapter()
rv_home.visibility = View.VISIBLE
if (it != null) {
adapter.setData(it.response.venues.sortedBy { myObject -> myObject.name })
searchData = it.response.venues.sortedBy { myObject -> myObject.name }
} else {
Toast.makeText(this, Constants.ERROR_MESSAGE_API, Toast.LENGTH_SHORT).show()
}
}
})
}
My ViewModelTest class:
#RunWith(AndroidJUnit4::class)
class VenueViewModelTest : TestCase() {
private lateinit var viewModel: VenueViewModel
#Before
public override fun setUp() {
super.setUp()
viewModel = VenueViewModel()
}
#Test
fun testVenueViewModel()
{
}
}
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)
}