Creación de reglas personalizadas para testear ViewModels con coroutinas

Clase 9 de 16Curso de Android Testing

Resumen

Testear ViewModels que utilizan coroutinas puede volverse repetitivo al configurar manualmente dispatchers en cada test. Crear una regla personalizada usando la clase TestWatcher de JUnit permite encapsular esta lógica repetitiva. Así, se genera un código más limpio, reutilizable y menos susceptible a errores en pruebas.

¿Por qué utilizar TestWatcher para testear coroutinas?

La clase TestWatcher de la librería JUnit identifica dos escenarios clave: el inicio y el fin de cada test. Al extenderla, se simplifica notablemente la configuración de coroutinas en los tests, automatizando acciones antes y después de su ejecución. Esto evita configurar manualmente Dispatchers.setMain() y Dispatchers.resetMain().

¿Cómo crear una regla personalizada para testear coroutinas?

Para crear nuestra propia regla, seguimos estos pasos:

  1. Creamos una clase llamada MainDispatcherRule dentro del folder utils.
  2. Extendemos de la clase TestWatcher.
  3. Definimos el dispatcher por defecto como UnconfinedTestDispatcher().
  4. Sobrescribimos métodos específicos:
  5. Método starting: llamado al inicio del test; configura el main dispatcher utilizando Dispatchers.setMain() con nuestro dispatcher definido.
  6. Método finished: llamado al finalizar el test; restablece dispatcher principal mediante Dispatchers.resetMain().

La clase en Kotlin se ve así:

@ExperimentalCoroutinesApi
class MainDispatcherRule(
    val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : TestWatcher() {

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

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

¿Cómo implementar esta regla en nuestros tests de ViewModel?

Al integrar la regla creada en nuestros tests:

  • Removemos configuraciones repetitivas y minimizamos la posibilidad de olvidar restablecer el dispatcher.
  • Reemplazamos las acciones manuales por nuestra regla, declarándola simplemente con @get:Rule.
  • Eliminamos llamados repetitivos a métodos como advanceUntilIdle() a menos que se utilicen dispatchers específicos que requieran su uso explícito.

Esto es cómo se vería su implementación:

@get:Rule
val mainDispatcherRule = MainDispatcherRule()

Al ejecutar los tests con esta nueva regla:

  • Si utilizamos UnconfinedTestDispatcher(), no necesitamos llamar a advanceUntilIdle().
  • Si utilizamos StandardTestDispatcher(), debemos añadir de nuevo advanceUntilIdle() para que los tests pasen.

La personalización de esta regla aporta facilidad y efectividad al testeo de ViewModels con coroutinas.