1. Información general
En este tutorial, veremos cómo escribir pruebas utilizando el soporte del marco en Spring Boot. Cubriremos las pruebas unitarias que se pueden ejecutar de forma aislada, así como las pruebas de integración que arrancarán el contexto de Spring antes de ejecutar las pruebas.
Si es nuevo en Spring Boot, consulte nuestra introducción a Spring Boot.
2. Configuración del proyecto
La aplicación que usaremos en este artículo es una API que proporciona algunas operaciones básicas en un recurso de empleado . Esta es una arquitectura en niveles típica: la llamada a la API se procesa desde el controlador al servicio y la capa de persistencia .
3. Dependencias de Maven
Primero agreguemos nuestras dependencias de prueba:
org.springframework.boot spring-boot-starter-test test 2.2.6.RELEASE com.h2database h2 test
El motor de arranque-pruebas de la primavera-arranque es la dependencia principal que contiene la mayoría de los elementos necesarios para nuestras pruebas.
H2 DB es nuestra base de datos en memoria. Elimina la necesidad de configurar e iniciar una base de datos real con fines de prueba.
4. Prueba de integración con @DataJpaTest
Vamos a trabajar con una entidad llamada Empleado, que tiene una identificación y un nombre como propiedades:
@Entity @Table(name = "person") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Size(min = 3, max = 20) private String name; // standard getters and setters, constructors }
Y aquí está nuestro repositorio usando Spring Data JPA:
@Repository public interface EmployeeRepository extends JpaRepository { public Employee findByName(String name); }
Eso es todo para el código de la capa de persistencia. Ahora vayamos a escribir nuestra clase de prueba.
Primero, creemos el esqueleto de nuestra clase de prueba:
@RunWith(SpringRunner.class) @DataJpaTest public class EmployeeRepositoryIntegrationTest { @Autowired private TestEntityManager entityManager; @Autowired private EmployeeRepository employeeRepository; // write test cases here }
@RunWith (SpringRunner.class) proporciona un puente entre las funciones de prueba de Spring Boot y JUnit. Siempre que utilicemos funciones de prueba de Spring Boot en nuestras pruebas de JUnit, se requerirá esta anotación.
@DataJpaTest proporciona una configuración estándar necesaria para probar la capa de persistencia:
- configurar H2, una base de datos en memoria
- configuración de Hibernate, Spring Data y DataSource
- realizar un @EntityScan
- activar el registro de SQL
Para realizar operaciones de base de datos, necesitamos algunos registros que ya están en nuestra base de datos. Para configurar estos datos, podemos usar TestEntityManager.
Spring Boot TestEntityManager es una alternativa al JPA EntityManager estándar que proporciona métodos que se usan comúnmente al escribir pruebas.
EmployeeRepository es el componente que vamos a probar.
Ahora escribamos nuestro primer caso de prueba:
@Test public void whenFindByName_thenReturnEmployee() { // given Employee alex = new Employee("alex"); entityManager.persist(alex); entityManager.flush(); // when Employee found = employeeRepository.findByName(alex.getName()); // then assertThat(found.getName()) .isEqualTo(alex.getName()); }
En la prueba anterior, estamos usando TestEntityManager para insertar un empleado en la base de datos y leerlo a través de la API de búsqueda por nombre.
La parte assertThat (…) proviene de la biblioteca Assertj, que viene incluida con Spring Boot.
5. Burlarse con @MockBean
Nuestro código de capa de servicio depende de nuestro repositorio .
Sin embargo, para probar la capa de servicio , no necesitamos saber ni preocuparnos por cómo se implementa la capa de persistencia:
@Service public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeRepository employeeRepository; @Override public Employee getEmployeeByName(String name) { return employeeRepository.findByName(name); } }
Idealmente, deberíamos poder escribir y probar nuestro código de capa de servicio sin conectar nuestra capa de persistencia completa.
Para lograr esto, podemos utilizar el soporte de simulación proporcionado por Spring Boot Test.
Primero echemos un vistazo al esqueleto de la clase de prueba:
@RunWith(SpringRunner.class) public class EmployeeServiceImplIntegrationTest { @TestConfiguration static class EmployeeServiceImplTestContextConfiguration { @Bean public EmployeeService employeeService() { return new EmployeeServiceImpl(); } } @Autowired private EmployeeService employeeService; @MockBean private EmployeeRepository employeeRepository; // write test cases here }
Para verificar la clase Service , necesitamos tener una instancia de la clase Service creada y disponible como @Bean para que podamos @Autowire en nuestra clase de prueba. Podemos lograr esta configuración usando la anotación @TestConfiguration .
Durante el escaneo de componentes, podemos encontrar que componentes o configuraciones creados solo para pruebas específicas se recogen accidentalmente en todas partes. Para ayudar a prevenir esto, Spring Boot proporciona la anotación @TestConfiguration que podemos agregar a las clases en src / test / java para indicar que no deben ser detectadas mediante el escaneo.
Otra cosa interesante aquí es el uso de @MockBean . Crea un Mock para el EmployeeRepository , que se puede usar para omitir la llamada al EmployeeRepository real :
@Before public void setUp() { Employee alex = new Employee("alex"); Mockito.when(employeeRepository.findByName(alex.getName())) .thenReturn(alex); }
Dado que la configuración está lista, el caso de prueba será más simple:
@Test public void whenValidName_thenEmployeeShouldBeFound() { String name = "alex"; Employee found = employeeService.getEmployeeByName(name); assertThat(found.getName()) .isEqualTo(name); }
6. Prueba unitaria con @WebMvcTest
Nuestro controlador depende de la capa de servicio ; incluyamos solo un método único por simplicidad:
@RestController @RequestMapping("/api") public class EmployeeRestController { @Autowired private EmployeeService employeeService; @GetMapping("/employees") public List getAllEmployees() { return employeeService.getAllEmployees(); } }
Dado que solo nos centramos en el código del controlador , es natural simular el código de la capa de servicio para nuestras pruebas unitarias:
@RunWith(SpringRunner.class) @WebMvcTest(EmployeeRestController.class) public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @MockBean private EmployeeService service; // write test cases here }
Para probar los controladores , podemos usar @WebMvcTest . Autoconfigurará la infraestructura Spring MVC para nuestras pruebas unitarias.
En la mayoría de los casos, @ WebMvcTest se limitará a arrancar un solo controlador. También podemos usarlo junto con @MockBean para proporcionar implementaciones simuladas para cualquier dependencia requerida.
@WebMvcTest también configura automáticamente MockMvc , que ofrece una forma poderosa de probar fácilmente los controladores MVC sin iniciar un servidor HTTP completo.
Habiendo dicho eso, escribamos nuestro caso de prueba:
@Test public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { Employee alex = new Employee("alex"); List allEmployees = Arrays.asList(alex); given(service.getAllEmployees()).willReturn(allEmployees); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].name", is(alex.getName()))); }
La llamada al método get (…) puede ser reemplazada por otros métodos correspondientes a verbos HTTP como put () , post () , etc. Tenga en cuenta que también estamos configurando el tipo de contenido en la solicitud.
MockMvc es flexible y podemos crear cualquier solicitud usándolo.
7. Prueba de integración con @SpringBootTest
Como sugiere el nombre, las pruebas de integración se centran en integrar diferentes capas de la aplicación. Eso también significa que no se trata de burlarse.
Idealmente, deberíamos mantener las pruebas de integración separadas de las pruebas unitarias y no deberían ejecutarse junto con las pruebas unitarias. Podemos hacer esto usando un perfil diferente para ejecutar solo las pruebas de integración. Un par de razones para hacer esto podrían ser que las pruebas de integración requieren mucho tiempo y pueden necesitar una base de datos real para ejecutarse.
Sin embargo, en este artículo, no nos centraremos en eso y, en su lugar, utilizaremos el almacenamiento de persistencia H2 en memoria.
Las pruebas de integración necesitan iniciar un contenedor para ejecutar los casos de prueba. Por lo tanto, se requiere una configuración adicional para esto; todo esto es fácil en Spring Boot:
@RunWith(SpringRunner.class) @SpringBootTest( SpringBootTest.WebEnvironment.MOCK, classes = Application.class) @AutoConfigureMockMvc @TestPropertySource( locations = "classpath:application-integrationtest.properties") public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @Autowired private EmployeeRepository repository; // write test cases here }
La anotación @SpringBootTest es útil cuando necesitamos arrancar todo el contenedor. La anotación funciona creando el ApplicationContext que se utilizará en nuestras pruebas.
Podemos usar el atributo webEnvironment de @SpringBootTest para configurar nuestro entorno de ejecución; estamos usando WebEnvironment.MOCK aquí para que el contenedor funcione en un entorno de servlet simulado.
A continuación, la anotación @TestPropertySource ayuda a configurar las ubicaciones de los archivos de propiedades específicos de nuestras pruebas. Tenga en cuenta que el archivo de propiedades cargado con @TestPropertySource anulará el archivo application.properties existente .
Los application-integrationtest.properties contiene los detalles para configurar el almacenamiento de persistencia:
spring.datasource.url = jdbc:h2:mem:test spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Si queremos ejecutar nuestras pruebas de integración contra MySQL, podemos cambiar los valores anteriores en el archivo de propiedades.
Los casos de prueba para las pruebas de integración pueden parecer similares a las pruebas unitarias de la capa del controlador :
@Test public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { createTestEmployee("bob"); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content() .contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].name", is("bob"))); }
La diferencia con las pruebas unitarias de la capa del controlador es que aquí no se simula nada y se ejecutarán escenarios de un extremo a otro.
8. Pruebas configuradas automáticamente
Una de las características asombrosas de las anotaciones configuradas automáticamente de Spring Boot es que ayuda a cargar partes de la aplicación completa y capas específicas de prueba de la base de código.
Además de las anotaciones mencionadas anteriormente, aquí hay una lista de algunas anotaciones ampliamente utilizadas:
- @WebF luxTest : Podemos usar la anotación @WebFluxTest para probar los controladores Spring WebFlux. A menudo se usa junto con @MockBean para proporcionar implementaciones simuladas para las dependencias requeridas.
- @JdbcTest : W e puede utilizar el @JdbcTest anotación para aplicaciones APP prueba, pero es para las pruebas que sólo requieren una fuente de datos. La anotación configura una base de datos incrustada en memoria y una JdbcTemplate.
- @JooqTest : para probar pruebas relacionadas con jOOQ, podemos usar la anotación @JooqTest , que configura un DSLContext.
- @DataMongoTest: To test MongoDB applications, @DataMongoTest is a useful annotation. By default, it configures an in-memory embedded MongoDB if the driver is available through dependencies, configures a MongoTemplate, scans for @Document classes, and configures Spring Data MongoDB repositories.
- @DataRedisTestmakes it easier to test Redis applications. It scans for @RedisHash classes and configures Spring Data Redis repositories by default.
- @DataLdapTest configures an in-memory embedded LDAP (if available), configures a LdapTemplate, scans for @Entry classes, and configures Spring Data LDAP repositories by default.
- @RestClientTest: We generally use the @RestClientTest annotation to test REST clients. It auto-configures different dependencies such as Jackson, GSON, and Jsonb support; configures a RestTemplateBuilder; and adds support for MockRestServiceServer by default.
9. Conclusion
In this article, we took a deep dive into the testing support in Spring Boot and showed how to write unit tests efficiently.
El código fuente completo de este artículo se puede encontrar en GitHub. El código fuente contiene muchos más ejemplos y varios casos de prueba.
Y si desea seguir aprendiendo sobre las pruebas, tenemos artículos separados relacionados con las pruebas de integración y las pruebas unitarias en JUnit 5.