Модульное тестирование приостанавливает работу функции для LiveData, но проходит для потока

#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
    }
  

Это работает, если в тестируемой функции нет задержки. Я думаю, что это не должно быть проблемой, если производственный код находится в стадии тестирования.