Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

413
Views
¿Cómo usar kotlinx.coroutines.withTimeout en kotlinx.coroutines.test.runTest?

Tengo una función de suspensión que realiza una llamada de descanso a una API externa que quiero que se agote después de 1 minuto.

 suspend fun makeApiCallWithTimeout(): List<ApiResponseData> = withTimeout(1.minutes) { apiCall() }

Estoy tratando de probarlo con Junit5 y kotlinx.coroutines.test 1.6.0 así:

 @Test fun `Test api call`() = runTest { val responseData = "[]" mockWebServer.enqueue(mockResponse(body = responseData) val result = sut.makeApiCallWithTimeout() advanceUntilIdle() assertEquals(0, result.size) }

Desafortunadamente, recibo errores que se ven así:

 Timed out waiting for 60000 ms kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 60000 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) (Coroutine boundary)

Parece que kotlinx.coroutines.test.runTest está avanzando el tiempo virtual en withTimeout sin darle tiempo para ejecutar su cuerpo. Ver ( https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/README.md#using-withtimeout-inside-runtest )

Desafortunadamente, la documentación no proporciona una forma de evitar esto.

Indique cómo probar esta función usando runTest.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Esto se debe a la omisión de retardo.

Aquí está usando runTest , que brinda capacidades de control de tiempo a su prueba. Para ello, este generador de corrutinas proporciona a un despachador una hora falsa que omite automáticamente los retrasos (desde la perspectiva del tiempo real), pero realiza un seguimiento interno de la hora falsa.

Desde el punto de vista de este despachador, todo lo que no tiene delay() s se ejecuta instantáneamente, mientras que las cosas que sí retrasan hacen que el tiempo falso progrese.

Sin embargo, esto no se puede usar con cosas que realmente toman tiempo real fuera del despachador de prueba, porque la prueba realmente no esperará. Entonces, en esencia, withTimeout se agota inmediatamente porque la apiCall() real probablemente se ejecuta fuera del despachador (y toma tiempo real).

Puede reproducir fácilmente este comportamiento de esta manera:

 @Test fun test() = runTest { withTimeout(1000) { // immediately times out apiCall() } } suspend fun apiCall() = withContext(Dispatchers.IO) { Thread.sleep(100) // not even 1s }

Normalmente hay 2 soluciones:

  • si desea seguir usando el tiempo controlado, debe asegurarse de estar usando el despachador de prueba en todo el código relevante. Esto significa que los lugares en su código donde usa ámbitos de corrutina personalizados o despachadores explícitos deberían permitir inyectar un despachador

  • si realmente no necesita tiempo controlado, puede usar runBlocking en lugar de runTest (en JVM) o seguir usando runTest pero ejecutar la prueba en otro despachador como Dispatchers.Default :

 fun test() = runTest { withContext(Dispatchers.Default) { // test code } }
over 4 years ago · Santiago Trujillo Report

0

Una adición a la respuesta de Joffrey.

Ejemplo de cómo inyectar el programador si desea seguir usando el tiempo controlado:

 @Test fun test() = runTest { val gw = MyGateway(testScheduler) withTimeout(1000) { gw.apiCall() } } class MyGateway(private val context: CoroutineContext = Dispatchers.IO) { companion object : Logging suspend fun apiCall() = withContext(context) { Thread.sleep(100) // not even 1s } }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!