Inspirado por una pregunta del usuario OBD7 sobre dónde encontrar información sobre JUnit, decidí hacer un pequeño post en donde haremos una prueba de integración y una prueba unitaria usando MockMVC y Junit. Antes de empezar es importante resaltar que probar nuestras aplicaciones es una parte vital de nuestro trabajo como programadores, ya que mediante las pruebas podemos estar más seguros de que lo hacemos está correcto, sirve como documentación para el futuro y además permite que nuestro sistema pueda evolucionar sin preocuparnos si un cambio que introdujimos dañó el código anterior.
Tipos de Pruebas
En este post nos concentraremos en dos tipos de prueba principalmente
Pruebas Unitarias Se prueban funcionalidades muy específicas del código, por ejemplo, un solo método de una clase; cualquier componente externo, distinto a la clase actual se debe simular, para evitar que este afecte la prueba.
Pruebas de Integración Estas garantinzan que un conjunto de componentes interactúen de manera adecuada. Por ejemplo en Spring se podría probar un endpoint y sus respectivos servicios y repositorios.
Implementando Pruebas en nuestro código
Vamos a generar nuestro proyecto usando la página https://start.spring.io/, solamente agregando soporte web, y dando clic en el botón de generar proyecto.
Una vez hayas generado el proyecto y lo hayas importado en tu IDE de preferencia, el primer paso es crear un componente que llamaremos GreetingService, cuyo único objetivo es retornar la cadena “Hola Mundo”.
import org.springframework.stereotype.Component;
@ComponentpublicclassGreetingService{
public String helloWorld(){
return"Hola Mundo";
}
}
El segundo paso es crear un controlador, que inyecte este servicio y retorne el valor del método helloWorld con un código de status 200.
@ControllerpublicclassGreetingController{
privatefinal GreetingService greetingService;
@AutowiredpublicGreetingController(GreetingService greetingService){
this.greetingService = greetingService;
}
@GetMapping("/hello")
public ResponseEntity sayHello(){
return ResponseEntity.ok(greetingService.helloWorld());
}
}
Pruebas Unitarias
Lo primero que probaremos será la clase GreetingService, esta no tiene ninguna dependencia por lo que es la más simple de probar. Lo primero a tener en cuenta es la anotación @Test, esta se debe poner en todos los métodos que ejecuten pruebas. También es importante resaltar el método Assert.assertEquals, que verifica que dos parámetros sean iguales, en este caso se verifica que esté retornando la cadena hola mundo. Junit provee métodos similares a este, para ver una lista más exhaustiva pueden entrar a JUnit Api.
import org.junit.Assert;import org.junit.Test;publicclassGreetingServiceUnitTest{
private GreetingService greetingService = new GreetingService();
@Test
publicvoid itShouldSayHolaMundo() {
Assert.assertEquals("Hola Mundo", greetingService.helloWorld());
}
}
Ahora probaremos el controlador, este es un poco más dificil de probar ya que tiene la dependencia con GreetingService, para ayudarnos con esto simularemos el GreetingService usando Mockito para evitar que esta clase afecte la prueba del controlador.
De este código es importante resaltar los métodos Mockito.mock y Mockito.when, los cuales permiten crear un objeto simulado y cambiar su comportamiento, en este caso hacemos que el GreetingService retorne Saludos en lugar de Hola Mundo (solo por razones demostrativas). La anotación @Before se usa en los métodos que inicializan los objetos antes de una prueba.
NOTA: También se podría usar la anotación @WebMvc para esta prueba, pero de esta manera se entiende mejor cómo funciona Junit junto a Mockito.
import com.davidfcalle.platzitesting.controller.GreetingController;import com.davidfcalle.platzitesting.service.GreetingService;import org.junit.Assert;import org.junit.Before;import org.junit.Test;import org.mockito.Mock;import org.mockito.Mockito;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;
public class GreetingControllerUnitTest {
private GreetingController controller;
private GreetingService greetingService;
@Before
public void setUp() {
greetingService = Mockito.mock(GreetingService.class);
controller = new GreetingController(greetingService);
}
@Test
public void itShouldReturnTheServiceValueWith200StatusCode() {
Mockito.when(greetingService.helloWorld()).thenReturn("Saludos");
ResponseEntity<String> httpResponse = controller.sayHello();
Assert.assertEquals(httpResponse.getStatusCode(), HttpStatus.OK);
Assert.assertEquals("Saludos", httpResponse.getBody());
}
}
Pruebas de Integración
Por último haremos una prueba tenga en cuenta ambos componentes al tiempo. Para esto usaremos MockMvc que es un utilitario de Spring para pruebas de integración.
De esta clase es importante resaltar la anotación @SpringBootTest, en la cual especificamos nuestra clase main 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.
Esta prueba fue diferente a la anterior ya que en esta se prueba que toda la aplicación esté bien y la interacción de sus componentes funcione como deseamos.
import org.junit.*;import org.junit.runner.*;import org.springframework.beans.factory.annotation.*;import org.springframework.boot.test.autoconfigure.web.servlet.*;import org.springframework.boot.test.mock.mockito.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.MockMvcBuilder;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.assertj.core.api.Assertions.*;import static org.mockito.BDDMockito.*;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = PlatziTestingApplication.class
)
public class PlatziTestingApplicationTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void testHelloEndpointIsOK() throws Exception {
this.mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hola Mundo"));
}
}
<h1>Conclusión</h1>
Siguiendo este tutorial tuviste una introducción práctica a testear aplicaciones de Spring, ahora podrás agregarle pruebas a tus aplicaciones para estar más tranquilo al momento de evolucionar tu sistema.
Lo que estoy viendo en tu ejemplo es que no puedo mezclar un ejemplo de spring con Mockito y spring boot con MockMvc. Hubiese sido bueno que dejaras el código de los dos ejemplos para visualizar la configuración de cada uno, ya que en el segundo me sale lo siguiente: IllegalStateException: Could not load CacheAwareContextLoaderDelegate.
Acá dejo el código del primero ejemplo, que involucra sólo las pruebas unitarias: https://github.com/cesardramirez/spring-junit-mockito
Hola Cesar,
revisé, el problema es que mezclas las versiones 2 y 4 de spring en el pom, desafortunadmente no subí el codigo que hice a github. Sin embargo, me tomé la libertad de mandar un PR en github par ayudarte a solucionar el problema y para que los demás lo usen como referencia.
Acá el PR: https://github.com/cesardramirez/spring-junit-mockito/pull/1
Para los que también les pasa esto, si generan el proyecto usando a https://start.spring.io/, no deberían tener este problema ya que crea el pom por nosotros con las dependencias correctas.
Hola, he realizado una Apirest en Spring Boot para hacer un CRUD y consumir con Angular, sin embargo quisiera saber en concreto que debe mostrar las pruebas que realice.
En la mayoría de los ejemplos veo que retornan un mensaje y no se pasa del ‘Hola mundo’, la pregunta es, que debe retornar el test?
Espero entiendan mi pregunta, gracias.