Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

141
Vistas
devolución de llamada de prueba unitariaFlow

Tengo una API basada en devolución de llamada como esta:

 class CallbackApi { fun addListener(callback: Callback) { // todo } fun removeListener(callback: Callback) { // todo } interface Callback { fun onResult(result: Int) } }

y una función de extensión que convierte la API en un flujo frío y caliente :

 fun CallbackApi.toFlow() = callbackFlow<Int> { val callback = object : CallbackApi.Callback { override fun onResult(result: Int) { trySendBlocking(result) } } addListener(callback) awaitClose { removeListener(callback) } }

¿Le importaría sugerir cómo escribir una prueba de unidad que asegure que la API se convierta correctamente en un flujo activo?

Aquí está mi intento. Por prueba y error, se me ocurrió esta solución.

 @Test fun callbackFlowTest() = runBlocking { val callbackApi = mockk<CallbackApi>() val callbackSlot = slot<CallbackApi.Callback>() every { callbackApi.addListener(capture(callbackSlot)) } just Runs every { callbackApi.removeListener(any()) } just Runs val list = mutableListOf<Int>() val flow: Flow<Int> = callbackApi.toFlow().onEach { list.add(it) } val coroutineScope = CoroutineScope(this.coroutineContext + SupervisorJob()) flow.launchIn(coroutineScope) yield() launch { callbackSlot.captured.onResult(10) callbackApi.removeListener(mockk()) // this was a misunderstanding }.join() assert(list.single() == 10) }

Pero no entiendo dos piezas de esta solución.

1- En ausencia de ese SupervisorJob() , parece que la prueba nunca terminará. Tal vez recolectar el flujo nunca termina por alguna razón, que no entiendo. Estoy alimentando la devolución de llamada capturada en una rutina separada.

2- Si elimino el cuerpo de launch que callbackSlot.captured.onResult(10) , la prueba fallará con este error UninitializedPropertyAccessException: lateinit property captured has not been initialized . Creo que el yield debería iniciar el flujo.

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

Aquí está la solución que encontré basada en las guías útiles de @Joffrey:

 @Test fun callbackFlowTestSolution() = runTest { val callbackApi = mockk<CallbackApi>() val callbackSlot = slot<CallbackApi.Callback>() every { callbackApi.addListener(capture(callbackSlot)) } just Runs every { callbackApi.removeListener(any()) } just Runs val itemsToSend = Array(9) { index -> index } // [0, 1, 2, 3, 4, 5, 6, 7, 8] val collectedItems = mutableListOf<Int>() val flow: Flow<Int> = callbackApi.toFlow().onEach { collectedItems.add(it) } val collectJob = launch { flow.collect() } advanceUntilIdle() // wait for callbackFlow builder to call addListener itemsToSend.forEach { callbackSlot.captured.onResult(it) } advanceUntilIdle() // wait for flow collection collectJob.cancel() advanceUntilIdle() // wait for awaitClose verify { callbackApi.removeListener(callbackSlot.captured) } assertArrayEquals(itemsToSend.toIntArray(), collectedItems.toIntArray()) }

Considere actualizar a kotlinx-coroutines-test:1.6.0 para usar runTest . En versiones anteriores, podemos usar runBlockingTest , pero está en desuso.


ACTUALIZACIÓN : si desea verificar que el flujo puede recopilar elementos uno por uno

 @Test fun callbackFlowTestSolution() = runTest { val callbackApi = mockk<CallbackApi>() val callbackSlot = slot<CallbackApi.Callback>() every { callbackApi.addListener(capture(callbackSlot)) } just Runs every { callbackApi.removeListener(any()) } just Runs val itemsToSend = Array(9) { index -> index } // [0, 1, 2, 3, 4, 5, 6, 7, 8] var lastCollectedItem: Int? = null val flow: Flow<Int> = callbackApi.toFlow().onEach { lastCollectedItem = it } val collectJob = launch { flow.collect() } advanceUntilIdle() // wait for callbackFlow builder to call addListener itemsToSend.forEach { callbackSlot.captured.onResult(it) advanceUntilIdle() // wait for flow collection assertEquals(it, lastCollectedItem) } collectJob.cancel() advanceUntilIdle() // wait for awaitClose verify { callbackApi.removeListener(callbackSlot.captured) } }
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda