JUnit es un entorno para poder crear pruebas unitarias. Es una biblioteca que podemos integrar en nuestro programa para comprobar automáticamente que nuestro programa esté bien hecho y que no tenga bugs o defectos.
Si bien podemos hacer manualmente una comprobación ejecutando el programa y viendo que se comporta como espera, o que vemos por pantalla los mensajes que tenemos que ver, a menudo sale más útil automatizarlo para poder pedirle al ordenador que ejecute una y otra vez las mismas funciones del programa y que compruebe que continúan devolviendo lo que tienen que devolver. De este modo, en vez de perder un rato probando manualmente el programa a cada cambio que hagamos, podemos pedirle al ordenador que en segundos nos haga cientos de comprobaciones si hace falta, incluso cada vez que modificamos una línea de código.
Las bibliotecas de testeo como JUnit se basan en un concepto denominado asertos (que estudiaremos posteriormente en este curso), mediante las cuales podemos comprobar que dos variables coinciden en valor. Por ejemplo, tenemos asertos que comprueban que dos expresiones tienen el mismo valor.
int expected = 4;
int actual = sum(2, 2);
assertEquals(expected, actual);
En este caso, assertEquals será un aserto, que toma dos expresiones o valores como parámetro y que, en este caso, provocará un error si los dos parámetros no son iguales. Más sobre asertos en el tutorial de creación de una prueba o en el capítulo dedicado a asertos este taller.
JUnit tiene un runner, que es el programa que corre los códigos que conforman las pruebas, que permite correr docenas, cientos o miles de funciones con código como el de este ejemplo en poco tiempo y de forma automatizada, algo que nos permite comprobar rápidamente y de una forma más fácil de repetir que nuestro código está bien desarrollado. Esto es una ventaja frente a simplemente escribir manualmente un main() para cada código nuevo que hayamos escrito y comprobar que funciona.
Sobre todo, en ocasiones olvidamos que algunas refactorizaciones o cambios en un módulo pueden provocar cambios inesperados en las clases donde ese módulo colabora o es instanciado, motivo por el cual cuando se hace un cambio en un código, hace falta asegurar que no se ha “roto” otra parte del programa debido al cambio que hemos hecho en primer lugar. A veces esto resulta contraintuitivo porque no comprendemos cómo un cambio en el módulo A puede provocar que haya errores en el módulo B, pero es una consecuencia directa de una mala organización de código, o simplemente, de un modelo de datos enrevesado.
Cualquier IDE te va a permitir hoy día automatizar la gestión de los runners de JUnit, permitiéndote de forma fácil crear clases de prueba para clases de código Java ya existentes, así como invocar runners y mostrar de forma visual los errores que se hayan podido producir. Esta es otra de las ventajas que tenemos con las pruebas automatizadas, de hecho: que vamos a tener reportes que nos enumeren todas las pruebas que se han lanzado, cuáles han salido bien y cuáles han salido mal.
Nuestra forma de trabajar con JUnit típicamente será lanzar la suite de tests, ver los tests que han fallado, actuar sobre esos tests, y volver a lanzar la suite de tests. La actuación sobre un test que ha fallado pasa normalmente por revisar el test, comprobar el código, y determinar si existe un bug o condición incorrecta en el código que haya podido propiciar un error en las salidas.
A lo largo de los próximos capítulos, veremos conceptos de JUnit, empezando por los más elementales, como la creación de pruebas, y terminando por otros más específicos y que nos pueden solucionar casos concretos.