Introducción

Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: el análisis, la documentación… los tests y las pruebas. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE y empezar a picar código, cuando muchas veces lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente.

Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.

Sin embargo, al igual que un buen análisis, diseño y planificación nos ahorra tiempo y evita errores a largo plazo, un buen diseño de los casos de prueba y su automatización contribuye a obtener un mejor resultado final. Además, nos garantiza que podremos detectar rápidamente si un nuevo evolutivo introduce errores que no habíamos contemplado.

Testing en Spring Boot

Spring Boot incorpora de serie varias herramientas que facilitan enormemente la implementación de los primeros tests. Entre ellas destacan:

  • JUnit 5: es un framework de testing que nos permite definir y ejecutar test en Java mediante anotaciones como @Test.
  • Spring Boot Test: conjunto de utilidades que facilita probar aplicaciones Spring cargando el contexto, inyectando dependencias y simulando peticiones HTTP.
  • Maven configurado para ejecutar test: Maven ejecuta automáticamente los test durante el ciclo de compilación, fallando el proceso si alguno no pasa y evitando la generación de artefactos incorrectos.

Primer test de Hello World

Para implementar los primeros tests nos vamos a basar en el Hello World que desarrollamos en el artículo anterior y que ya hemos compilado con Maven.

Al crear la estructura básica del proyecto con Spring Initializr e incluir la dependencia Spring Web, ya deberíamos tener en el pom.xml la dependencia spring-boot-starter-webmvc-test, que nos proporciona todo lo necesario para estos primeros tests.

La aplicación arranca

Se trata de un test de contexto, anotado con @SpringBootTest, que ya tendremos creado automáticamente en la estructura del proyecto dentro de la carpeta src/test. En nuestro caso se denomina SpringBootHelloWorldApplicationTests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package dev.juanfbermejo.SpringBootHelloWorld;  
  
import org.junit.jupiter.api.Test;  
import org.springframework.boot.test.context.SpringBootTest;  
  
@SpringBootTest  
class SpringBootHelloWorldApplicationTests {  
  
    @Test  
    void contextLoads() {  
    }  
  
}

Este test no contiene lógica alguna, pero precisamente ahí reside su valor. Comprueba que el contexto de Spring arranca correctamente y, si falla, indica que existe un problema grave en la configuración de la aplicación.

Testear un endpoint sencillo: Hello World

El siguiente paso es diseñar e implementar un test para nuestro endpoint /hello. Aunque se trata de un endpoint muy sencillo, ya podemos validar tres aspectos básicos:

  1. Que el endpoint existe bajo la URL
  2. Que responde con código HTTP 200
  3. Que devuelve el contenido esperado

El código del test es el siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package dev.juanfbermejo.SpringBootHelloWorld;  
  
import org.junit.jupiter.api.Test;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.context.SpringBootTest;  
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;  
import org.springframework.test.web.servlet.MockMvc;  
  
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;  
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;  
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
  
@SpringBootTest  
@AutoConfigureMockMvc  
public class HelloControllerTest {  
  
    @Autowired  
    private MockMvc mockMvc;  
  
    @Test  
    void helloEndpointReturnsHelloWorld() throws Exception {  
        mockMvc.perform( get("/hello") )  
                .andExpect( status().isOk() )  
                .andExpect( content().string("Hello World from Spring Boot!") );  
    }  
}

Elementos que se usan en el test

Anotaciones de Spring y JUnit:

  • @Autowired: le indica a Spring que inyecte automáticamente una instancia de MockMvc en el test.
  • @Test: anotación de JUnit 5 que marca un método como test y permite que el framework lo ejecute durante la fase de testing.
  • @AutoConfigureMockMvc: indica a Spring Boot que configure automáticamente MockMvc, permitiéndonos simular peticiones HTTP a los endpoints sin arrancar un servidor real.

Herramientas para simular peticiones HTTP:

  • MockMvc: utilidad de Spring para simular llamadas HTTP (GET, POST, etc.) contra los controladores y verificar sus respuestas.
  • get(): construye una petición HTTP de tipo GET contra la ruta indicada, en nuestro ejemplo /hello.
  • perform(): ejecuta la petición HTTP simulada y definida previamente. Devuelve el resultado para poder hacer comprobaciones sobre la respuesta.

Validación de la respuesta:

  • andExpect(): define una expectativa sobre la respuesta obtenida; si no se cumple, el test falla.
  • status() e isOk(): comprueban que el código de estado HTTP de la respuesta sea 200 (OK).
  • content(): permite validar el contenido del cuerpo de la respuesta.

Ejecutar los test con Maven

Una vez configurados los tests, podemos ejecutarlos desde el propio IDE o, al igual que para la construcción del artefacto, desde la línea de comandos utilizando Maven.

Desde un terminal, situados en la carpeta raíz del proyecto, ejecutamos:

mvn test

Maven compilará el código y lanzará todos los tests definidos en el proyecto, mostrando el resultado en la consola.

Terminal que muestra que el test ha dado resultado positivo

Si forzamos un error -por ejemplo, cambiando el texto esperado en el cuerpo de la respuesta- y ejecutamos de nuevo el comando, veremos cómo el test falla. La salida por consola indicará qué test ha fallado, qué se esperaba que ocurriera y cuál ha sido el resultado real, proporcionando información muy útil para entender el problema y corregirlo.

Terminal que muestra que el test ha dado resultado negativo

Conclusiones

En este artículo hemos dado el primer paso en la incorporación de tests automatizados a nuestra API Spring Boot. Partiendo de un ejemplo muy sencillo, hemos visto cómo validar que la aplicación arranca correctamente y cómo comprobar el comportamiento de un endpoint HTTP mediante tests automatizados.

Hasta ahora, en la serie hemos creado una API básica, la hemos compilado con Maven y la hemos preparado para su ejecución. Con estos primeros tests añadimos una capa fundamental de seguridad que nos permitirá evolucionar el código con mayor confianza.

En los siguientes artículos iremos ampliando este enfoque, incorporando tests más completos y sentando las bases para su integración en un proceso de integración continua (CI), donde estos tests se ejecutarán automáticamente en cada cambio del código.