Hemos terminado de escribir un modulo dentro del proyecto. Este modulo es el de validaciones, y es totalmente plano, esto hace que pasarles unos test sea muy sencillo, El desarrollo lo podeis encontrar en el articulo anterior de Desarrollo de un CRUD, en el canal de YouTube, o en GitHub (desaconsejado), si os da pereza escribirlo, pero lo vais a necesitar para poder empezar a realizar los test unitarios
Para que molestarnos en hacer Test
Por muy seguro que estemos de lo bien que programamos, cada día la realidad nos muestra que cometemos errores, y, aunque los lenguajes y los IDEs intentan minimizarlos, siempre se nos van escapando alguno, que antes o después, puede crear una mala imagen de la aplicacion, de la empresa, de la web, de la aplicación, … y como consecuencia una mala imagen nuestra. Realmente, manejamos informacion a veces delicada, a veces critica, y, debemos hacer todos los esfuerzos necesarios para minimizar esos problemas (aunque siempre podrán existir).
Los problemas, normalmente , no surgen de que hagamos algo mal, si no de que no hemos previsto todas las posibilidades que se pueden dar con lo datos que manejan nuestro código, y, esa combinación seguro que ocurrirá, provocando un error, luego necesitamos una herramienta que nos permita simular esas combinaciones de una forma rapida, para asi probar nuestro codigo de una forma cómoda, (que alguien no tenga que pasar horas en el terminal probando combinaciones)
Otra fuente de errores, todavía mas odiosa, es cuando nos dan una aplicacion que funciona perfectamente, para que modifiquemos un pequeño detalle, y, naturalmente que nosotros vamos a probar esa corrección hasta la saciedad, pero… si esa modificación, afecta a una función que esta 100 lineas mas abajo del codigo, y por la que se pasa en muy contadas ocasiones… un buen dia seremos responsable de un error en un sitio que nunca habíamos tocado. Entonces, nuestra herramienta mágica deberia poder realizar pruebas sobre todo el aplicativo, cada vez que alguien lo modifica, aunque sea una tonteria.
Bien, os acabo de describir Junit, un paquete pensado para poder realizar test, y dejarlos guardados, para que los podamos repetir siempre que queramos
El paquete Junit5
Este articulo no esta pensado para extendernos en las clases de test que hay, podemos verlas mas adelante; como introducción, solo quiero hablar de los test unitarios, en los que solo interviene en la prueba una función; nosotros le damos unos datos, y comprobamos nos de el resultado que nosotros sabemos que es el correcto. y con eso, y un muchísimo de imaginación, tenemos resuelto el problema.
Junit5 tiene muchas anotaciones y asserts disponibles; os invito a que las reviseis en junit.org
Como he dicho antes, vamos a realizar el test de la clase que construimos en el articulo anterior y lo realizaremos con este framework muy habitual en Java, que es Junit 5
Si abrimos STS con la app descargada, veremos el siguiente esquema.
Realmente, nosotros vamos a escribir el módulo RutinasTest.java, que utilizaremos para comprobar si nuestra clase Rutinas funciona bien.
Lo primero de todo, observad que ya tenemos un directorio src/test/java, para todos los package de test y se debe replicar exactamente los existentes en el directorio de trabajo src/main/java. Esto nos permite suprimir las clases de test en la generación de produccion, pero nos deja reconocer fácilmente el package
Programando el test
La clase de test la podemos definir manualmente, pero si nos ayudan un poco mejor, de forma que si apoyamos el ratón en el modulo a testear y utilizamos el botón derecho…
Nos aparece la pantalla de creación con
Nos aparece seleccionado New Junit Jupiter, que es el nombre como se reconoce también a la version 5 de Junit
El sistema ya nos ofrece correctamente el resto de la informacion, por lo que podemos pulsar en Next Para ir a
La idea es que seleccionemos los métodos para los que vamos a realizar test, y que normalmente, serán todos los de la clase, por lo que marco el checkbox situado al lado de Rutinas y pulsamos Finish
En unos segundos, nos aparece el módulo de test preparado para que podamos escribir nuestras instrucciones
Cada uno de estos métodos que ha creado, son para que nosotros escribamos las instrucciones para realizar el test de la función descrita en el nombre. Por ejemplo, en la linea 11 deberemos probar el método isVacio, y asi sucesivamente
La anotación @Test define que esa función es de test, por lo que no debe devolver nada
Veamos como resolvemos este test. El método espera recibir una string, y ha de contestar true si llega una string nula o vacía, y falso, para el resto de casos.
Podemos definir tres constantes String con esas características, luego, al principio de la clase, podemos decir
String STRING_NULA;
final String STRING_VACIA = "";
final String STRING_CON_DATOS = "Hola";
Ahora ya podemos escribir el test, enviando cada uno de esos datos, e indicando la respuesta que esperamos (True o False)
@Test
void testIsVacio() {
assertTrue(Rutinas.isVacio(STRING_NULA));
assertTrue(Rutinas.isVacio(STRING_VACIA));
assertFalse(Rutinas.isVacio(STRING_CON_DATOS));
}
Es bastante claro, podríamos hacer tantas comprobaciones como queramos, indicando, siempre, la respuesta que esperamos
Sin embargo, si lo queremos hacer asi, dentro de un test, el sistema detiene la ejecución cuando encuentra un error, por lo que en una pasada, no veríamos todos los test; eso lo podemos evitar usando el assertAll
@Test
void testIsVacio() {
assertAll(
() -> assertTrue(Rutinas.isVacio(STRING_NULA)),
() -> assertTrue(Rutinas.isVacio(STRING_VACIA)),
() -> assertFalse(Rutinas.isVacio(STRING_CON_DATOS))
);
}
Asi, hacemos que se realicen todas las pruebas, independientemente del resultado
Ahora, podemos iniciar la ejecución, cursor de ratón encima de RutinasTest y botón derecho
Veremos que se ejecuta el test y en una ventana nos aparece el resultado
Como vemos, el test (testIsVacio) ha pasado, y solo quedan los test de los métodos que no hemos escrito todavía. A partir de este momento, podemos ir creando cada test e ir probando, solo tened en cuenta que no debemos modificar la clase bajo test, para poder realizar el test, en la clase bajo test, solo deberemos corregir los errores detectados
Aparte del assertTrue y assertFalse que ya habeis visto utilizar, tenemos otros assert como:
- assertEquals – Se indica la rutina y el valor que espera, y comprueba que sean iguales
assertEquals(2, calculator.add(1, 1)); - assertNotNull – Comprueba el parámetro dentro de la funcion no sea nulo
assertNotNull(lastName); - assertThrows – Comprueba que la funcion lance el error indicado
Exception exception = assertThrows(ArithmeticException.class, () -> calculator.divide(1, 0)); assertEquals(«/ by zero», exception.getMessage());
Las anotaciones
Ya hemos visto que los métodos destinados a ser de test, van precedido por la anotación @Test, pero debemos tener en cuenta algunas otras
- @BeforeAll – el método anotado se ejecutará antes de todos
los @Test
y se debe escribir en un método static - @AfterAll – el método anotado se ejecutará después de todos
los @Test
y se debe escribir en un método static - @BeforeEach – el método anotado se ejecutará antes cada
@Test
,@RepeatedTest
,@ParameterizedTest
o@TestFactory
- @AfterEach – el método anotado se ejecutará después de cada
@Test
,@RepeatedTest
,@ParameterizedTest
o@TestFactory
- @ParameterizedTest – El método de test recibirá parámetros
- @ParameterizedTest(name = «caso año {0] es erróneo.»)
- @ValueSource(ints = { -1, -4 })
- void if_it_is_negative(int year) { }
- @RepeatedTest – Permite repetir un mismo test un numero de veces determinado. Se puede establecer el numero de veces que se autoriza que falle (failureThreshold) antes de dar el test por erróneo. Revisar la documentación de Junit5
Cuando lanzamos un test, no podemos garantizar el orden en que se ejecutaran los distintos métodos, Normalmente, no deberia tener importancia ya que cada test lo deberíamos realizar en un ambiente que garantizase que es único: pero si necesitamos imponer secuencia, podemos anotar el método con
- @TestMethodOrder – (A nivel de clase) Garantiza la ejecución de los métodos en el orden previsto por la anotación @Order(n) a nivel de método.
Otra anotacion
- @DisplayName – Podemos asignar nombres adecuados a los test
Podemos desactivar clases y/o métodos concretos, anteponiendo la anotación
- @Disabled(«motivo por el que esta desactivado la clase o el método concreto»)
Cuando utilizamos una de estas anotaciones, aparte de los parámetros que cada una acepte, podemos incluir un parámetro mas, que es «name«, y que establece de forma dinámica el nombre de test.
En los test parametrizados, podemos añadir con {n} el parámetro que nos interese, y em los test de repetición, podemos indicar {currentRepetition} y {totalRepetition}
El test de correo electrónico
Vamos a realizar el test del método que comprueba el correo electronico. Sabemos que el test solo realiza comprobaciones sintacticas del campo, por lo que debemos generar unas cuantas String que contengan posibles correos electronicos correctos y erroneos, y, para eso, al principio de la clase, puedo añadir líneas como estas
final String CORREO_ELECTRONICO_CORRECTO = "[email protected]";
final String CORREO_ELECTRONICO_ERRONEO_1 = "migarcia.recursosformacion.com";
final String CORREO_ELECTRONICO_ERRONEO_2 = "migarcia@recursosformacion";
final String CORREO_ELECTRONICO_ERRONEO_3 = "@recursosformacion.com";
final String CORREO_ELECTRONICO_ERRONEO_4 = "migarcia@";
El nombre de la cadena ya me indica si el literal se tiene que validar como correcto o erróneo, y también os indico que deberia haber previsto mas valores correctos, y eso lo hare en la version de YouTube
Ahora, ya podemos modificar el método testIsEmailValido, para retirar el mensaje de fail y escribir, por ejemplo
@Test
void testIsEmailValido() {
assertAll(() -> assertTrue(Rutinas.isEmailValido(CORREO_ELECTRONICO_CORRECTO)),
() -> assertFalse(Rutinas.isEmailValido(CORREO_ELECTRONICO_ERRONEO_1)),
() -> assertFalse(Rutinas.isEmailValido(CORREO_ELECTRONICO_ERRONEO_2)),
() -> assertFalse(Rutinas.isEmailValido(CORREO_ELECTRONICO_ERRONEO_3)),
() -> assertFalse(Rutinas.isEmailValido(CORREO_ELECTRONICO_ERRONEO_4)));
}
Probamos para ver que pasa, y continuamos
Para el resto de métodos, esta todo explicado, por lo que podéis resolverlo vosotros, pero… en el siguiente articulo veremos otras formas mas ingeniosas de resolverlo
Conclusión
Este articulo es parte de Visión de conjunto con Spring y teneis un desarrollo con mas detalle en youTube, y, aunque es conveniente que intentéis escribirlo TODO vosotros, si queréis renunciar a ello, lo teneis tambien en GitHub
La explicación en YouTube:
En el siguiente, abordaremos otra forma de realizar los test
Relacionado
Descubre más desde Recursos para formacion
Suscríbete y recibe las últimas entradas en tu correo electrónico.