Вы можете увидеть пример моей проблемы:
ViewModel:
Код: Выделить всё
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class TestViewmodel: ViewModel() {
private val _event = MutableSharedFlow()
private val _state = MutableStateFlow(TestState())
val state = _state.asStateFlow()
init {
viewModelScope.launch {
_event.collect{
handleEvent(it)
}
}
}
private fun handleEvent(event: TestEvent){
if(event == TestEvent.GetData){
getTestData()
}
}
private fun getTestData(){
viewModelScope.launch(Dispatchers.IO){
_state.update {
it.copy(
loading = true
)
}
// delay(100)
val data = getApiData()
_state.update {
it.copy(
loading = false
)
}
// delay(100)
handleApiData(data)
}
}
private fun handleApiData(list: List){
_state.update {
it.copy(
data = list
)
}
}
private suspend fun getApiData(): List{
delay(1000)
return listOf("ffefe","ffefe","fdfdfd")
}
fun setEvent(event: TestEvent){
viewModelScope.launch {
_event.emit(event)
}
}
}
data class TestState(
val loading: Boolean = false,
val data: List = emptyList()
)
sealed interface TestEvent{
data object GetData: TestEvent
}
Код: Выделить всё
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.extension.ExtendWith
@OptIn(ExperimentalCoroutinesApi::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestViewModelTest(){
private lateinit var viewModel: TestViewmodel
@BeforeAll
fun setUp(){
Dispatchers.setMain(Dispatchers.Unconfined)
viewModel = TestViewmodel()
}
@AfterAll
fun afterAll(){
Dispatchers.resetMain()
}
@Test
fun testEmit() = runTest {
val state = viewModel.state
viewModel.setEvent(TestEvent.GetData)
val result = state.drop(2).first()
result.data.size shouldBe 3
}
@Test
fun testEmitWithTurbine() = runTest {
val state = viewModel.state
state.test {
viewModel.setEvent(TestEvent.GetData)
awaitItem()
awaitItem()
awaitItem().data.size shouldBe 3
}
}
@Test
fun testEmitByGoogle() = runTest {
val state = viewModel.state
val result = mutableListOf()
val collectionJob = launch {
state.toList(result)
}
viewModel.setEvent(TestEvent.GetData)
result[2].data.size shouldBe 3
collectionJob.cancel()
}
}
Я заметил, что если я ставлю задержку после каждого обновления состояния, тест пройдет успешно, но это не очень хорошее решение.
Я пробовал некоторые идеи из документов Google, но они не сработали. Я не помогу. Хотя тест не завершился с ошибкой UncompletedCoroutinesError, он не пройден. Это не удалось, потому что свойство состояния сохранило только первый эмитент, по крайней мере, так казалось.
Я также пытался использовать библиотеку Turbine, но результат был тот же.
Когда я отлаживал код, я заметил, что утверждение вызывается до того, как произойдет второй/третий выброс. Это объясняет, почему у меня всегда есть только 1 элемент в списке, но я понятия не имею, почему это происходит.< /п>
Подробнее здесь: https://stackoverflow.com/questions/782 ... ow-in-test