Proceso de Unit Testing

Hablemos de Unit Testing. Este tema reaparece cada par de semanas en el grupo de Android Chile, por lo que parece interesante hablar de él.

Cuando se habla de Unit Testing existen varias opciones, como unit testing, integration testing, UI testing… todas son opciones válidas, pero en este post voy a hablar de qué es Unit Testing en general, y el por qué quieres tener unit testing en tu aplicación.

Voy a explicar esto con escenarios de experiencias que me han ocurrido en mi carrera.

Tu nueva aplicación – Día 1.

Digamos que comienzas a hacer una aplicación nueva, y tu eres el único desarrollador en el proyecto. Esta aplicación es una aplicación bancaria, que solo te deja crear una cuenta nueva en el banco, y ver el saldo de tu cuenta.

La aplicación tiene una serie de requisitos, pero uno de esos es que cuando alguien cree una cuenta nueva, la aplicación revisa si la persona es mayor de 18 años. Si la persona tiene 18 años, o más, la cuenta puede ser creada. Si la persona tiene 17 años, o menos, la cuenta no puede ser creada. Super simple, pero es un requisito legal que es muy importante para tu empresa.

Como la aplicación es pequeña, y tu eres el único desarrollador, tú sabes de que en alguna parte de tu código, existe una cosa que revisa la edad. Digamos que la edad es verificada con algo así:

public boolean puedeCrearCuenta(Usuario usuario) {
    if (usuario.edad > 17) {
        return true;
    }
    return false;
}

La forma específica de cómo se revisa la edad es irrelevante. Lo que nos importa es que si pasamos un objeto de tipo Usuario, podemos ver si ese usuario puede crear una cuenta.

La aplicación es lanzada al público, y todo sale bien. Tu sabes que esa parte del código es importante, pero es una verificación tan pequeña que no es documentada ni probada.

Tu aplicación – 6 meses después

Tu aplicación está siendo utilizada por muchos usuarios, y el banco le quiere agregar muchas otras habilidades. Lamentablemente tu no puedes hacer todo, por lo que el banco contrata 2 desarrolladores más, o contrata con otra empresa para que ellos puedan crear las habilidades nuevas de la aplicación.

Tu verificación de edad, que escribiste hace 6 meses, continúa en el código, pero como nunca fue documentada, y está escrita en una forma muy básica, los desarrolladores nuevos modifican el código.

Como los desarrolladores nuevos ven el nombre de “puedeCrearCuenta” piensan de que esta verificación puede tener muchas opciones…por lo que ellos le agregan mas cosas.

public boolean puedeCrearCuenta(Usuario usuario) {
    if (usuario.edad > 17 && usuario.sueldo > 100000) {
        return true;
    }
    return false;
}

Ahora tu verificación de edad no solo revisa la edad, sino que también revisa el sueldo.

La aplicación compila, y nadie se da cuenta de la diferencia…hasta que tienes usuarios quejándose.

Unit Test al rescate

Si hubiéramos usado unit test, tal vez hubiéramos creado una par de pruebas como éstas:

public void testCrearCuentaAdulto() {
    Usuario usuario = new Usuario();
    usuario.nombre = "Eduardo Flores";
    usuario.edad = 18;
    assertTrue(puedeCrearCuenta(usuario));
}
public void testCrearCuentaMenorDeEdad() {
    Usuario usuario = new Usuario();
    usuario.nombre = "Eduardo Flores";
    usuario.edad = 17;
    assertFalse(puedeCrearCuenta(usuario));
}

Estas 2 pruebas son super simples. Lo único que estamos haciendo es crear un nuevo objeto de tipo Usuario, le damos un nombre al usuario y le damos una edad.

La primera prueba válida a el usuario de 18 años, y por lo tanto puede crear una cuenta. La segunda prueba revisa de que un usuario de 17 años no pueda crear una cuenta.

Ambas pruebas deberían pasar, sin importar el sueldo.

Qué logramos con nuestro Unit Test?

Es entendible de que en este momento tu digas “es obvio de que ambas pruebas van a pasar. Yo escribí el código, y yo escribí las pruebas. Y ambas pruebas son extremadamente simples!”
Y tendrías toda la razón, y es muy bueno que pase las pruebas.

La verdad es que Unit Testing no tiene nada de magia ni nada específico de código que nos importe. Comúnmente las pruebas de Unit Test son así de simples: creas un objeto, y pruebas un elemento específico de tu código.

En donde Unit Test es increíblemente poderoso es en el proceso de cómo desarrollamos la aplicación.

Es decir, Unit Testing es 90% proceso y 10% código…y el que escribas es comúnmente irrelevante.

Si tu aplicación hubiera tenido unit tests, como las 2 propuestas en este post, cuando los desarrolladores nuevos modifiquen el código del método puedeCrearCuenta(), una de las 2 pruebas va a fallar porque no estamos pasando un sueldo. Esto hubiera sido una prueba muy simple y rápida para ver que el código agregado a puedeCrearCuenta() no debería ir ahi, o simplemente no funciona.

Como crear el proceso

Depende de tu empresa, existen 2 formas en cómo verificar tu código con tus pruebas de unit test:

  1. Puedes hacer correr tu código de unit test arbitrariamente. Un ejemplo de esto es hacer correr las pruebas de unit test cada cierta cantidad de días, o antes de lanzar una actualización (o aplicación nueva)
  2. Puedes usar una aplicación a nivel de creación de tu build para correr tus pruebas de unit test. Nosotros usamos Jenkins, y cada vez que alguien escribe código nuevo a el repositorio, jenkins corre la aplicación y las pruebas de unit test. Si alguna prueba falla, le manda un email a todo el equipo.

El único cuasi-requisito de unit test es de que prueben 1 sola cosa específica. No te sirve de nada si la prueba de unit test revisa la edad, un nombre valido, una dirección correcta, etc… ya que si la prueba falla, el reporte enviado a todos es muy vago.

Conclusion

Espero de que este post te sirva para entender de que Unit Test no es requerido, pero es parte de un proceso que te puede ayudar a eliminar errores lógicos.
Unit testing también sirve mucho para verificar de que las cosas que funcionaron hace un año sigan funcionando hoy.

Te entendería de que pensaras de que puedes evitar el uso de Unit Test si usas mejor nombre para tu código, o comentarios, o documentación, pero lamentablemente eso comúnmente no sirve a largo plazo. Piensa de que si tus comentarios son en español, y tu empresa contrata con una empresa Alemana o Rusa o Inglesa, los contratistas no van a entender nada de tu documentación.

Aun así, si tu documentación fuera válida, no dice nada de cómo implementar el método. Que pasaría si en vez de revisar un integer (17) revisáramos en contra de Date() o en milliseconds? Es posible de que cambiemos la implementación, y tengamos un error lógico de cual es el límite de edad con la nueva implementación.

Eduardo.

Comenta este post