Невозможно захватить все выбросы из StateFlow при тестировании с помощью Turbine.Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Невозможно захватить все выбросы из StateFlow при тестировании с помощью Turbine.

Сообщение Anonymous »

Я пытаюсь протестировать свои модели представления, которые предоставляют общедоступный StateFlow через оператор stateIn:

Код: Выделить всё

    private val _uiState: MutableStateFlow =
MutableStateFlow(createInitialState(savedStateHandle))

val uiState: StateFlow = _uiState
.onStart { onViewSubscribed() }
.stateIn(
scope = viewModelScope,
started = sharingStarted,
initialValue = _uiState.value
)

Я использую турбину для улавливания выбросов и утверждения их:

Код: Выделить всё

    @Test
fun `Test that tries to capture each emittion separately`() = runTest {

viewModel.stateFlow.test {
val initial = awaitItem()
assertEquals(initial.isLoading, false)

viewModel.setStates()
val loading = awaitItem()
assertEquals(loading.isLoading, true)
val loaded = awaitItem()
assertEquals(loaded.isLoading, false)
val loadingAgain = awaitItem()
assertEquals(loadingAgain.isLoading, true)
val loadedAgain = awaitItem()
assertEquals(loadedAgain.isLoading, false)
val loadingAgain2 = awaitItem()
assertEquals(loadingAgain2.isLoading, true)
}

}

Это проверяет эту функцию:

Код: Выделить всё

    fun setStates() {
viewModelScope.launch {
updateStateFlow { copy(isLoading = true) }
updateStateFlow { copy(isLoading = false) }
updateStateFlow { copy(isLoading = true) }
updateStateFlow { copy(isLoading = false) }
updateStateFlow { copy(isLoading = true) }
}
}

И я переопределяю основной диспетчер в тестах с помощью этого расширения JUnit5:

Код: Выделить всё

@ExperimentalCoroutinesApi
class MainDispatcherExtension(
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
Dispatchers.setMain(testDispatcher)
}

override fun afterEach(context: ExtensionContext?) {
Dispatchers.resetMain()
}
}
Проблема в том, что я не могу захватывать каждую эмиссию отдельно, Турбина выдает исключение «Нет значения, созданного за 3 секунды» (на самом деле она фиксирует только последнюю эмиссию, поэтому остальные, ожидающие эмиссии, естественно, вызывают таймаут). Но если я добавлю задержку между выбросами, я смогу захватывать их так, как захочу (отдельно, и у каждого есть разные утверждения):

Код: Выделить всё

    fun setStates() {
viewModelScope.launch {
updateStateFlow { copy(isLoading = true) }
delay(1)
updateStateFlow { copy(isLoading = false) }
delay(1)
updateStateFlow { copy(isLoading = true) }
delay(1)
updateStateFlow { copy(isLoading = false) }
delay(1)
updateStateFlow { copy(isLoading = true) }
}
}
Но, как вы видите, это изменение производственного кода только для того, чтобы иметь возможность его протестировать, и это отвратительно, а не жизнеспособный вариант.
Я прочитал эту статью о «объединении» потока состояний и попытался применить ее решение, изменив свой диспетчер runTest на StandardTestDispatcher:

Код: Выделить всё

@ExperimentalCoroutinesApi
class MainDispatcherExtension(
private val testDispatcher: TestDispatcher = StandardTestDispatcher()
) : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
Dispatchers.setMain(testDispatcher)
}

override fun afterEach(context: ExtensionContext?) {
Dispatchers.resetMain()
}
}
и, таким образом, согласно статье Turbine будет собирать данные в UnconfinedTestDispatcher, и мне нужно иметь возможность захватывать каждую эмиссию, но это не сработало. В чем причина? Можете ли вы прояснить это для меня? Возможно, я смогу отметить автора статьи, чтобы узнать, не понял ли я какую-то ее часть.

Подробнее здесь: https://stackoverflow.com/questions/798 ... th-turbine
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»