TimeoutCancellationException при запуске тестов для сопрограммы Retrofit в потоке KotlinAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 TimeoutCancellationException при запуске тестов для сопрограммы Retrofit в потоке Kotlin

Сообщение Anonymous »

У меня есть репозиторий, который создает поток, в котором я выдаю результат приостановки метода Retrofit. В приложении это работает, но я хотел бы запустить тесты кода.
В своих тестах я использую kotlinx-coroutines-test v1.6.0 и MockWebServer v4.9.3. Когда я пытаюсь запустить тест, я получаю:

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

Timed out waiting for 1000 ms
kotlinx.coroutines.TimeoutCancellationException:  T i m e d   o u t   w a i t i n g   f o r   1 0 0 0   m s < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . T i m e o u t K t . T i m e o u t C a n c e l l a t i o n E x c e p t i o n ( T i m e o u t . k t : 1 8 4 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . T i m e o u t C o r o u t i n e . r u n ( T i m e o u t . k t : 1 5 4 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t D i s p a t c h e r . p r o c e s s E v e n t $ k o t l i n x _ c o r o u t i n e s _ t e s t ( T e s t D i s p a t c h e r . k t : 2 3 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t C o r o u t i n e S c h e d u l e r . t r y R u n N e x t T a s k ( T e s t C o r o u t i n e S c h e d u l e r . k t : 9 5 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t C o r o u t i n e S c h e d u l e r . a d v a n c e U n t i l I d l e ( T e s t C o r o u t i n e S c h e d u l e r . k t : 1 1 0 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t . r u n T e s t C o r o u t i n e ( T e s t B u i l d e r s . k t : 2 1 2 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t . r u n T e s t C o r o u t i n e ( U n k n o w n   S o u r c e ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t $ r u n T e s t $ 1 $ 1 . i n v o k e S u s p e n d ( T e s t B u i l d e r s . k t : 1 6 7 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t $ r u n T e s t $ 1 $ 1 . i n v o k e ( T e s t B u i l d e r s . k t ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t $ r u n T e s t $ 1 $ 1 . i n v o k e ( T e s t B u i l d e r s . k t ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s J v m K t $ c r e a t e T e s t R e s u l t $ 1 . i n v o k e S u s p e n d ( T e s t B u i l d e r s J v m . k t : 1 3 ) < b r   / >         ( C o r o u t i n e   b o u n d a r y ) < b r   / >         a t   a p p . c a s h . t u r b i n e . C h a n n e l B a s e d F l o w T u r b i n e $ a w a i t E v e n t $ 2 . i n v o k e S u s p e n d ( F l o w T u r b i n e . k t : 2 4 7 ) < b r   / >         a t   a p p . c a s h . t u r b i n e . C h a n n e l B a s e d F l o w T u r b i n e $ w i t h T i m e o u t $ 2 . i n v o k e S u s p e n d ( F l o w T u r b i n e . k t : 2 1 5 ) < b r   / >         a t   a p p . c a s h . t u r b i n e . C h a n n e l B a s e d F l o w T u r b i n e . a w a i t I t e m ( F l o w T u r b i n e . k t : 2 5 2 ) < b r   / >         a t   o g b e . e v a . p r o m p t . h o m e . H o m e R e p o s i t o r y T e s t $ c u r r e n t T a s k   w h e n   s e r v e r   r e s p o n d s   w i t h   e r r o r   e m i t s   f a i l u r e $ 1 $ 1 . i n v o k e S u s p e n d ( H o m e R e p o s i t o r y T e s t . k t : 9 0 ) < b r   / >         a t   a p p . c a s h . t u r b i n e . F l o w T u r b i n e K t $ t e s t $ 2 . i n v o k e S u s p e n d ( F l o w T u r b i n e . k t : 8 6 ) < b r   / >         a t   o g b e . e v a . p r o m p t . h o m e . H o m e R e p o s i t o r y T e s t $ c u r r e n t T a s k   w h e n   s e r v e r   r e s p o n d s   w i t h   e r r o r   e m i t s   f a i l u r e $ 1 . i n v o k e S u s p e n d ( H o m e R e p o s i t o r y T e s t . k t : 8 9 ) < b r   / >         a t   k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t $ r u n T e s t C o r o u t i n e $ 2 . i n v o k e S u s p e n d ( T e s t B u i l d e r s . k t : 2 0 8 ) < b r   / >         ( C o r o u t i n e   c r e a t i o n   s t a c k t r a c e ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . i n t r i n s i c s . U n d i s p a t c h e d K t . s t a r t C o r o u t i n e U n d i s p a t c h e d ( U n d i s p a t c h e d . k t : 1 8 4 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s J v m K t $ c r e a t e T e s t R e s u l t $ 1 . i n v o k e S u s p e n d ( T e s t B u i l d e r s J v m . k t : 1 3 ) < b r   / >         a t   a p p / / k o t l i n . c o r o u t i n e s . j v m . i n t e r n a l . B a s e C o n t i n u a t i o n I m p l . r e s u m e W i t h ( C o n t i n u a t i o n I m p l . k t : 3 3 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . D i s p a t c h e d T a s k . r u n ( D i s p a t c h e d T a s k . k t : 1 0 6 ) < b r   / >         a t   a p p / / k o t l i n x . c o r o u t i n e s . t e s t . T e s t B u i l d e r s K t _ _ T e s t B u i l d e r s K t . r u n T e s t $ d e f a u l t ( T e s t B u i l d e r s . k t : 1 6 1 ) < b r  />    at app//kotlinx.coroutines.test.TestBuildersKt.runTest$default(Unknown Source)
at app//ogbe.eva.prompt.TestCoroutineRule.runTest(TestCoroutineRule.kt:26)
at app//ogbe.eva.prompt.home.HomeRepositoryTest.currentTask when server responds with error emits failure(HomeRepositoryTest.kt:84)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base@11.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@11.0.11/java.lang.reflect.Method.invoke(Method.java:566)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base@11.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@11.0.11/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by:  kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at app//kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:184)
at app//kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:154)
at app//kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:23)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTask(TestCoroutineScheduler.kt:95)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.advanceUntilIdle(TestCoroutineScheduler.kt:110)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:212)
at app//kotlinx.coroutines.test.TestBuildersKt.runTestCoroutine(Unknown Source)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
at app//kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at app//kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at app//kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
at app//kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at app//kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at app//kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at app//kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at app//kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at app//kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:12)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest(TestBuilders.kt:166)
at app//kotlinx.coroutines.test.TestBuildersKt.runTest(Unknown Source)
... 50 more
У меня нет возможности выполнить тестовые утверждения, а это именно то, что я хочу сделать. Это просто терпит неудачу из-за этой неожиданной ошибки.
Я проверил, вызывая случайные функции приостановки в потоке и запуская фиктивный сервер вне моей функции потока. Оба из них завершатся без ошибки тайм-аута, но когда я объединяю поток, тест и модернизацию, появляется ошибка тайм-аута.
Код репозитория:

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

class HomeRepository @Inject constructor(
@IoDispatcher ioDispatcher: CoroutineDispatcher,
private val promptService: PromptService,
) {
val currentTask = flow {
try {
val response = promptService.getSchedule(1) // Suspending Retrofit method that fails the tests
if (response.isSuccessful) {
val schedule = response.body()
if (schedule == null) {
Log.e(TAG, "Get schedule response has empty body")
emit(LoadState.Failure())
} else {
emit(LoadState.Data(schedule.tasks.first()))
}
} else {
Log.e(
TAG,
"Server responded to get schedule request with error: ${response.message()}"
)
emit(LoadState.Failure())
}
} catch (e: Exception) {
Log.e(TAG, "Could not get schedule from server", e)
emit(LoadState.Failure())
}
}
.flowOn(ioDispatcher)

companion object {
private val TAG = HomeRepository::class.simpleName
}
}
Тестовый код:

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

@OptIn(ExperimentalCoroutinesApi::class)
class HomeRepositoryTest {
@get:Rule
val testCoroutineRule = TestCoroutineRule()

private val mockWebServer = MockWebServer()

@Before
fun setUp() {
mockWebServer.start()
}

@After
fun tearDown() {
mockWebServer.shutdown()
}

@Test
fun `currentTask when server responds with error emits failure`() = testCoroutineRule.runTest {
mockWebServer.enqueue(MockResponse().setResponseCode(500))

val homeRepository = createRepository()

homeRepository.currentTask.test {
expectThat(awaitItem()).isFailure()
awaitComplete()
}
}

private fun createRepository(promptService: PromptService = createPromptService()) =
HomeRepository(testCoroutineRule.testDispatcher, promptService)

private fun createPromptService(): PromptService {
val client = OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(PromptService::class.java)
}
}
Код правила:

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

@OptIn(ExperimentalCoroutinesApi::class)
class TestCoroutineRule : TestWatcher() {
val testDispatcher = StandardTestDispatcher()

private val testScope = TestScope(testDispatcher)

override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}

override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}

fun runTest(block: suspend TestScope.() -> Unit) =
testScope.runTest(testBody = block)
}
Как протестировать поток, использующий Retrofit, не получив ошибки тайм-аута?

Подробнее здесь: https://stackoverflow.com/questions/717 ... ne-in-a-ko
Ответить

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

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

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

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

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