问题
I load data in recycleView in advance. In order to do that I have following code in onCreate() of Activity :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupUI()
setupViewModel()
observeViewModel()
if (savedInstanceState == null) {
mainViewModel.userIntent.offer(MainIntent.FetchUser)
}
}
As you see I offer() when savedInstanceState is null, The problem is when we have process death ( you can simply create it by activating Do not keep activities in developer option), reload of data will not be triggered.
another option is to use it inside init block of ViewModel, but problem is I want to have bellow unit test which I can verify all three states :
@Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
runBlockingTest {
`when`(apiService.getUsers()).thenReturn(emptyList())
val apiHelper = ApiHelperImpl(apiService)
val repository = MainRepository(apiHelper)
val viewModel = MainViewModel(repository)
viewModel.state.asLiveData().observeForever(observer)
viewModel.userIntent.send(MainIntent.FetchUser)
}
verify(observer, times(3)).onChanged(captor.capture())
verify(observer).onChanged(MainState.Idle)
verify(observer).onChanged(MainState.Loading)
verify(observer).onChanged(MainState.Users(emptyList()))
}
If I use the init block option as soon as ViewModel initialized, send or offer will be called while observeForever did not be used for LiveData in the above unit test.
Here is my ViewModel class :
class MainViewModel(
private val repository: MainRepository
) : ViewModel() {
val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
private val _state = MutableStateFlow<MainState>(MainState.Idle)
val state: StateFlow<MainState>
get() = _state
init {
handleIntent()
}
private fun handleIntent() {
viewModelScope.launch {
userIntent.consumeAsFlow().collect {
when (it) {
is MainIntent.FetchUser -> fetchUser()
}
}
}
}
private fun fetchUser() {
viewModelScope.launch {
_state.value = MainState.Loading
_state.value = try {
MainState.Users(repository.getUsers())
} catch (e: Exception) {
MainState.Error(e.localizedMessage)
}
}
}
}
What could be the solution for the above scenarios?
回答1:
The only solution that I found is moving fetchUser method and another _state as MutableStateFlow to Repository layer and observeForever it in Repository for local unit test, as a result I can send or offer userIntent in init block off ViewModel.
I will have following _state in ViewModel :
val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
private val _state = repository.state
val state: StateFlow<MainState>
get() = _state
来源:https://stackoverflow.com/questions/65388332/right-place-to-offer-or-send-channel-in-mvi-pattern