#android #unit-testing #android-livedata #android-viewmodel
#Android #модульное тестирование #android-livedata #android-viewmodel
Вопрос:
Я загружаю данные в RecycleView заранее. Для этого у меня есть следующий код в onCreate() действия :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupUI()
setupViewModel()
observeViewModel()
if (savedInstanceState == null) {
mainViewModel.userIntent.offer(MainIntent.FetchUser)
}
}
Как вы видите, offer()
когда savedInstanceState имеет значение null, проблема в том, что когда у нас происходит смерть процесса (вы можете просто создать его, активировав параметр «Не сохранять действия в параметре разработчика»), перезагрузка данных не будет запущена.
другой вариант — использовать его внутри init
блока ViewModel, но проблема в том, что я хочу провести модульный тест ниже, в котором я могу проверить все три состояния :
@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()))
}
Если я использую опцию init block, как только инициализируется ViewModel, send
или offer
будет вызван, пока observeForever
не использовался для LiveData в приведенном выше модульном тесте.
Вот мой класс ViewModel :
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)
}
}
}
}
Каким может быть решение для вышеуказанных сценариев?
Ответ №1:
Единственное решение, которое я нашел fetchUser
, — это метод перемещения, а другой _state
как MutableStateFlow на уровень репозитория и observeForever
в репозиторий для локального модульного тестирования, в результате я могу отправить или предложить userIntent
в init заблокировать ViewModel.
У меня будет следующее _state
в ViewModel :
val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
private val _state = repository.state
val state: StateFlow<MainState>
get() = _state