Ahora bien, ¿Qué tiene Spring Boot para nuestro tema de pruebas unitarias? Si vemos la configuración del pom.xml del proyecto generado por Spring Initializr, veremos que ya se incluye una dependencia para pruebas, específicamente la que se muestra a continuación.

Es importante resaltar que el starter spring-boot-starter-test, proporciona diversas librerías útiles para las pruebas como JUnit, Mockito, AssertJ, Hamcrest y demás, también es posible incluir dependencias opcionales, en nuestro proyecto sin especificar la versión, siendo estas:
Para nuestro ejemplo, crearemos un proyecto maven llamado springCuadrado desde el servicio web initializr, tal como se hizo anteriormente, incluyendo la dependencia Spring Web; este proyecto, se encargará de hacer varios cálculos para una figura geométrica (Cuadrado), cuenta con una clase cuadradoService y cuadradoController.
La clase cuadradoService contiene el siguiente código
package com.springboot.spring.model; import org.springframework.stereotype.Component; @Component public class CuadradoService { //Calcula el area de un cuadrado en centímetros cruadrados public double calcularArea (double lado) { return lado*lado; } //Calcula el perimetro de un cuadrado en centímetros public double calcularPerimetro (double lado) { return lado*4; } //Calcula la diagonal de un cuadrado en centímetros public double calcularDiagonal (double lado) { return (lado*Math.sqrt(2)); } }
Como pueden notar, esta clase se conforma por métodos que permiten calcular el área, perímetro y diagonal de un cuadrado; antes de la clase, hay una anotación @Component, que indica a Spring que la clase anotada (CuadradoService) es uno de sus objetos.
Siguiendo con la presentación de las clases, cuadradoController tiene el siguiente código
package com.springboot.spring.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class CuadradoController { private CuadradoService cuadradoService; @Autowired public CuadradoController(CuadradoService cuadradoService) { this.cuadradoService = cuadradoService; } @PostMapping("/area") public ResponseEntity<Double> calcularAreaCuadrado(double lado) { return ResponseEntity.ok(cuadradoService.calcularArea(lado)); } @PostMapping("/perimetro") public ResponseEntity<Double> calcularPerimetroCuadrado(double lado) { return ResponseEntity.ok(cuadradoService.calcularPerimetro(lado)); } @PostMapping("/diagonal") public ResponseEntity<Double> calcularDiagonalCuadrado(double lado) { return ResponseEntity.ok(cuadradoService.calcularDiagonal(lado)); } }
Vemos que esta clase, esta anotada con @Controller, perteneciente a los estereotipos de Spring, tal como el anterior. Este realiza las tareas de controlador y gestión de la comunicación entre el usuario y el aplicativo, tenemos además dos anotaciones dentro de la clase, la primera es @Autowired, que permite hacer la inyección de dependencias, en este caso entre CuadradoService y CuadradoController.
Nota: Para realizar test, es recomendado realizar el @Autowired a nivel de constructores, como en la clase vista.
Y la segunda anotación es el @PostMapping, utilizada para asignar solicitudes HTTP de tipo POST.
Una vez configuradas las clases vamos a generar las clases Test para cada una, para el caso de la clase CuadradoService, que no tiene ninguna dependencia con otras, utilizaremos JUnit5 el cual se acopla a esta situación, para esta clase, realizamos el test para cada método, por medio de la opción ya vista en capítulos anteriores de JUnit, JUnit Test Case y seguimos las instrucciones dadas, seleccionando así los métodos de esta.
Una vez realizado los pasos y de tener nuestra clase Test, procedemos a añadir nuestro código prueba.
package com.springboot.spring.test; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class CuadradoServiceTest { CuadradoService cuadrado = new CuadradoService(); @Test void testCalcularArea() { double lado=5; double expected = 25; double actual = cuadrado.calcularArea(lado); assertEquals(expected, actual); } @Test void testCalcularPerimetro() { double lado=5; double expected = 20; double actual = cuadrado.calcularPerimetro(lado); assertEquals(expected, actual); } @Test void testCalcularDiagonal() { double lado=5; double expected = 7.0710678118654752440084436210485; double actual = cuadrado.calcularDiagonal(lado); assertEquals(expected, actual); } }
Solo nos basta correrla y veremos que el resultado que nos arroja JUnit, es exitoso.

Para la clase CuadradoController, utilizaremos Mockito, dado que CuadradoController posee una dependencia con CuadradoService, la cual debemos mockear o aislar a través de un doble de prueba como Mock.
En el siguiente código, mock se está instanciando de forma estática, en el método setUp() de la anotación @BeforeEach; además se está haciendo uso del método when de Mockito para cambiar el comportamiento del objeto simulado.
package com.springboot.spring.test; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; import static org.mockito.Mockito.mock; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; class CuadradoControllerTest { private CuadradoController cuadradoController; private CuadradoService cuadradoService; @BeforeEach public void setUp() { cuadradoService = mock(CuadradoService.class); cuadradoController = new CuadradoController(cuadradoService); } @Test void testCalcularAreaCuadrado() { when(cuadradoService.calcularArea(10)).thenReturn(100.0); ResponseEntity<Double> httpResponse = cuadradoController.calcularAreaCuadrado(10); assertEquals (HttpStatus.OK, httpResponse.getStatusCode()); assertEquals(100, httpResponse.getBody()); } @Test void testCalcularPerimetroCuadrado() { when(cuadradoService.calcularPerimetro(10)).thenReturn(40.0); ResponseEntity<Double> httpResponse = cuadradoController.calcularPerimetroCuadrado(10); assertEquals (HttpStatus.OK, httpResponse.getStatusCode()); assertEquals(40, httpResponse.getBody()); } @Test void testCalcularDiagonalCuadrado() { when(cuadradoService.calcularDiagonal(10)).thenReturn(14.142135623730950488016887242097); ResponseEntity<Double> httpResponse = cuadradoController.calcularDiagonalCuadrado(10); assertEquals (HttpStatus.OK, httpResponse.getStatusCode()); assertEquals(14.142135623730950488016887242097, httpResponse.getBody()); } }
Si ejecutamos nuestra prueba, veremos que también nos arroja un resultado exitoso.

Con lo anterior, hemos abarcado, el tema de pruebas unitarias en Spring Boot, usando JUnit y Mockito, ahora, realizaremos una prueba que incluya ambos componentes o clases, haciendo uso de MockMvc; es de resaltar que si se habla estrictamente de MockMvc, este estaría entre la frontera de pruebas unitarias y pruebas de integración, por lo que no son tests unitarios porque los endpoints (permiten monitorear e interactuar con la aplicación) se prueban de forma integrada con un contenedor MVC con dependencias e inputs simulados, pero tampoco podríamos tomar estos test como de integración, ya que si se definen correctamente con MockMvc, sólo probaremos un único componente con una plataforma simulada, pero no una combinación de componentes, como normalmente sería una prueba de integración.
En el código, apreciamos unas anotaciones @ExtendWith() equivalente a @RunWith en JUnit4, el cual sirve para invocar la clase en lugar del ejecutor por defecto, la anotación @SpringBootTest, en la cual especificamos nuestra clase main (Application) para cargar el contexto de Spring, en el setup de la prueba se puede ver que inicializamos MockMvc para después usarlo llamando al endpoint especificado y hacer una prueba sobre el código de status y el contenido del resultado.
En cuanto a WebApplicationContext, esta es una extensión del ApplicationContext simple que tiene algunas características adicionales necesarias para las aplicaciones web, se diferencia de un ApplicationContext normal en que es capaz de resolver temas, y que sabe con qué Servlet está asociado al tener un enlace al ServletContext, el WebApplicationContext está enlazado en el ServletContext, y al usar métodos estáticos en la clase RequestContextUtils, siempre puede buscar el WebApplicationContext si necesita acceder a él.
Con MockMvc somos capaces que el código de respuesta se corresponde con el esperado (200, 201…), incluso cuando el código se asigna fuera del método del controlador o post-procesado por una anotación/aspect.
En este caso, haremos uso de un solo método testCalcularAreaEndPoint(), con el cual analizaremos los códigos de respuesta (response codes) y las cabeceras, mandaremos por el método HTTP POST a la URL (/área) el parámetro (lado, 5) y esperamos que el status de la respuesta sea exitosa y que la respuesta que viene sea 25.0, equivalente al área de un cuadrado cuyos lados es de 5.
package com.springboot.spring.test; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = Application.class) class ApplicationTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @BeforeEach public void setUp() { this.mockMvc= MockMvcBuilders.webAppContextSetup(wac).build(); } @Test void testCalcularAreaEndPoint() throws Exception { this.mockMvc.perform(post("/area").param("lado","5")) .andExpect(status().isOk()) .andExpect(content().string("25.0")); } }
Finalmente, si lo ejecutamos, tendremos como respuesta un succesfully.

Y con esto, damos por terminado nuestro curso de pruebas unitarias. Esperamos que haya sido de utilidad y que lo aprendido pueda ser replicado en sus futuros proyectos.