Testing de Flows con Turbine
Clase 10 de 16 • Curso de Android Testing
Resumen
La prueba de flujos de datos en aplicaciones Android es un aspecto crucial para garantizar la calidad del software. Turbine se presenta como una herramienta poderosa que simplifica este proceso, permitiéndonos capturar y validar cada emisión de datos de manera ordenada y efectiva. Esta capacidad resulta especialmente valiosa cuando necesitamos verificar que nuestros view models emiten estados en una secuencia específica, asegurando así el comportamiento correcto de nuestra aplicación.
¿Cómo probar flujos de datos con Turbine?
Turbine es una librería diseñada específicamente para probar flujos de datos en Kotlin, facilitando la observación y validación de cada valor emitido durante un test. Cuando trabajamos con arquitecturas basadas en estados, como MVI o MVVM, necesitamos asegurarnos de que nuestros view models emitan los estados correctos en el orden adecuado.
La función principal que nos proporciona Turbine es test()
, que nos permite capturar cada emisión del flujo y realizar verificaciones sobre ella. Dentro de este contexto, podemos utilizar awaitItem()
para esperar y capturar cada valor emitido, permitiéndonos hacer aserciones específicas sobre cada estado.
Implementación de pruebas con Turbine
Para implementar pruebas efectivas con Turbine, seguimos estos pasos:
- Configuramos nuestro test utilizando la función
test()
sobre el flujo que queremos probar. - Utilizamos
awaitItem()
para capturar cada emisión del flujo. - Realizamos aserciones sobre cada emisión capturada.
- Manejamos todas las emisiones o utilizamos funciones como
cancelAndIgnoreRemainingEvents()
si solo nos interesan algunas.
Veamos un ejemplo práctico:
@Test
fun givenStateWhenLoadProfileThenStateUpdate() = runTest {
// Utilizamos test() para capturar las emisiones del flujo
viewModel.state.test {
// Capturamos la primera emisión (estado inicial)
val emission1 = awaitItem()
Truth.assertThat(emission1.isLoading).isFalse()
// Ejecutamos la acción que generará nuevas emisiones
viewModel.loadProfile()
// Capturamos la segunda emisión (estado de carga)
val emission2 = awaitItem()
Truth.assertThat(emission2.isLoading).isTrue()
// Capturamos la tercera emisión (estado final con datos)
val emission3 = awaitItem()
Truth.assertThat(emission3.isLoading).isFalse()
Truth.assertThat(emission3.profile).isEqualTo(repository.profileToReturn)
}
}
¿Por qué es importante validar cada estado en un flujo?
Cuando trabajamos con view models que emiten estados, es crucial verificar no solo el estado final, sino también los estados intermedios. Esto nos permite asegurar que la experiencia del usuario sea la correcta durante todo el proceso, mostrando indicadores de carga cuando corresponde y actualizando la interfaz en el momento adecuado.
En nuestro ejemplo, validamos tres estados importantes:
- Estado inicial: Verificamos que
isLoading
sea falso antes de realizar cualquier acción. - Estado de carga: Comprobamos que
isLoading
cambie a verdadero cuando se inicia la carga de datos. - Estado final: Confirmamos que
isLoading
vuelva a falso y que los datos del perfil se hayan cargado correctamente.
Manejo de emisiones no consumidas
Un aspecto importante al trabajar con Turbine es asegurarse de consumir todas las emisiones del flujo durante el test. Si no lo hacemos, Turbine lanzará un error indicando que hay eventos no consumidos (Unconsumed events found
).
Para manejar esta situación, tenemos dos opciones:
- Consumir todas las emisiones: Utilizar
awaitItem()
para cada emisión esperada. - Ignorar emisiones restantes: Usar
cancelAndIgnoreRemainingEvents()
si solo nos interesan algunas emisiones específicas.
// Si solo nos interesan las dos primeras emisiones
viewModel.state.test {
val emission1 = awaitItem()
// Verificaciones sobre emission1
val emission2 = awaitItem()
// Verificaciones sobre emission2
// Ignoramos cualquier emisión adicional
cancelAndIgnoreRemainingEvents()
}
¿Cómo prepararnos para pruebas con datos reales?
Las pruebas que hemos visto hasta ahora utilizan repositorios falsos que simulan datos, lo cual es útil para probar la lógica de negocio. Sin embargo, en aplicaciones reales, los datos suelen provenir de peticiones de red, lo que introduce variables adicionales como latencia, errores de conexión o respuestas inesperadas.
Para probar estos escenarios, necesitamos herramientas que simulen respuestas HTTP reales. MockWebServer es una de estas herramientas, que nos permite:
- Simular respuestas HTTP con diferentes códigos de estado.
- Introducir retrasos para probar cómo se comporta nuestra aplicación con latencia.
- Devolver diferentes cuerpos de respuesta para probar distintos escenarios.
Esta aproximación nos permite realizar pruebas más completas y realistas, asegurando que nuestra aplicación se comporte correctamente incluso en condiciones adversas de red.
Turbine, combinado con herramientas como MockWebServer, nos proporciona un conjunto poderoso para probar exhaustivamente nuestras aplicaciones Android, garantizando una experiencia de usuario fluida y libre de errores.
¿Has utilizado Turbine en tus proyectos? ¿Qué otras herramientas utilizas para probar flujos de datos en Android? Comparte tu experiencia en los comentarios.