#android #unit-testing #junit #android-livedata #kotlin-coroutines
#Android #модульное тестирование #junit #android-livedata #kotlin-сопрограммы
Вопрос:
Я пытаюсь написать модульный тест для функции приостановки, которая выдает 2 значения, первое — это что-то вроде загрузки, второе — фактические данные. Существует некоторая несогласованность, и я не понимаю, почему это происходит. Чтобы было проще, вот небольшой пример с выдачей нескольких чисел (тип выданного не должен иметь значения).
class Foo(private val dispatcher: CoroutineDispatcher) {
suspend fun barLiveData(count: Int): LiveData<Int> = liveData(dispatcher) {
(1..count).forEach {
emit(it)
//delay(500)
}
}
suspend fun barFlow(count: Int): Flow<Int> = flow {
(1..count).forEach {
emit(it)
//delay(500)
}
}
}
Две функции приостановки, первая возвращает LiveData, вторая возвращает поток. В противном случае эти функции одинаковы. Если задержки нет, результаты модульных тестов отличаются. Если в функции задержка не менее 1 мс barLiveData
, модульный тест проходит.
У меня есть эти модульные тесты.
class FooTest {
@get:Rule
val instantExecutorRule = InstantTaskExecutorRule()
@get:Rule
val mainCoroutineRule = MainCoroutineRule()
val foo = Foo(mainCoroutineRule.testDispatcher)
@Test
fun `test barLiveData`() = runBlockingTest {
val values = foo.barLiveData(count).asFlow().take(count).toList()
println("test barLiveData result is ${values}")
}
@Test
fun `test barFlow`() = runBlockingTest {
val values = foo.barFlow(count).take(count).toList()
println("test barFlow result is ${values}")
}
companion object {
const val count = 5
}
}
TestCoroutineDispatcher используется в качестве диспетчера для класса Foo, MainCoroutineRule устанавливает диспетчера Main, сбрасывает его и вызывает cleanupTestCoroutines.
Когда я запускаю эти тесты, test barFlow
проходит, test barLiveData
завершается с java.lang.IllegalStateException: This job has not completed yet
ошибкой.
Если в функции задержка не менее 1 мс barLiveData
, test barLiveData
передайте. Тест test barFlow
проходит всегда.
Я делаю что-то не так? Должен ли тест быть написан по-другому?
Спасибо.
Редактировать: предлагаемое решение.Я до сих пор не понимаю, почему результат отличается, но у меня есть решение моей проблемы. Когда я использую observerForever, я могу получить все значения, которые добавляются в LiveData.
val values = mutableListOf<T>()
observeForever{
values.add(it)
}
Из этого списка можно проверить, являются ли состояния данных правильными и в правильном порядке.
Я не уверен, является ли observeForever в модульном тестировании проблемой или нет. Но в любом случае, вот некоторая функция расширения LiveData, которая удаляет наблюдателя
fun <T> LiveData<T>.getAllValues(): List<T> {
val values = mutableListOf<T>()
val observer = object: Observer<T> {
override fun onChanged(t: T) {
values.add(t)
}
}
this.observeForever(observer)
removeObserver(observer)
return values
}
Это работает, если в тестируемой функции нет задержки. Я думаю, что это не должно быть проблемой, если производственный код находится в стадии тестирования.