SpringBoot: Testing con Spring Test

Aprende a realizar testing efectivo en Spring Boot con Spring Test. Domina pruebas unitarias, integración, mocking y TestContainers.

Aprende SpringBoot GRATIS y certifícate

Testing con Spring Test

El testing es una práctica fundamental en el desarrollo de aplicaciones Spring Boot que garantiza la calidad y confiabilidad del código. Spring Framework proporciona un conjunto completo de herramientas de testing a través de Spring Test, que se integra perfectamente con el ecosistema de Spring Boot para facilitar la creación de pruebas efectivas.

Fundamentos del testing en Spring Boot

Spring Boot incluye por defecto el starter de testing que proporciona todas las dependencias necesarias para escribir pruebas. Este starter incluye JUnit 5, Mockito, AssertJ y las utilidades específicas de Spring Test, creando un entorno completo para diferentes tipos de pruebas.

La arquitectura de testing en Spring Boot se basa en el concepto de contexto de aplicación. Spring Test puede cargar un contexto completo o parcial de la aplicación, permitiendo probar componentes de forma aislada o en conjunto según las necesidades específicas de cada prueba.

@SpringBootTest
class ApplicationTests {
    
    @Test
    void contextLoads() {
        // Esta prueba verifica que el contexto de Spring se carga correctamente
    }
}

Tipos de pruebas en Spring Boot

Spring Boot facilita la implementación de diferentes niveles de testing mediante anotaciones especializadas. Cada tipo de prueba está diseñado para verificar aspectos específicos de la aplicación con el nivel de integración apropiado.

Pruebas unitarias

Las pruebas unitarias se centran en probar componentes individuales de forma aislada. Spring Test proporciona utilidades para crear mocks y configurar el contexto mínimo necesario.

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    void shouldCreateUser() {
        // Arrange
        User user = new User("Juan", "juan@email.com");
        when(userRepository.save(any(User.class))).thenReturn(user);
        
        // Act
        User result = userService.createUser("Juan", "juan@email.com");
        
        // Assert
        assertThat(result.getName()).isEqualTo("Juan");
        assertThat(result.getEmail()).isEqualTo("juan@email.com");
    }
}

Pruebas de integración

Las pruebas de integración verifican la interacción entre múltiples componentes. Spring Boot proporciona anotaciones específicas para cargar partes del contexto de aplicación según el tipo de integración que se desea probar.

@DataJpaTest
class UserRepositoryTest {
    
    @Autowired
    private TestEntityManager entityManager;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void shouldFindUserByEmail() {
        // Arrange
        User user = new User("Ana", "ana@email.com");
        entityManager.persistAndFlush(user);
        
        // Act
        Optional<User> found = userRepository.findByEmail("ana@email.com");
        
        // Assert
        assertThat(found).isPresent();
        assertThat(found.get().getName()).isEqualTo("Ana");
    }
}

Anotaciones principales de Spring Test

Spring Test proporciona un conjunto de anotaciones especializadas que simplifican la configuración de diferentes tipos de pruebas. Cada anotación carga únicamente los componentes necesarios, optimizando el tiempo de ejecución de las pruebas.

@SpringBootTest carga el contexto completo de la aplicación, ideal para pruebas de integración end-to-end:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    void shouldReturnUserList() {
        ResponseEntity<String> response = restTemplate.getForEntity("/api/users", String.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }
}

@WebMvcTest se enfoca en la capa web, cargando únicamente los controladores y componentes relacionados:

@WebMvcTest(UserController.class)
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void shouldReturnUser() throws Exception {
        User user = new User("Carlos", "carlos@email.com");
        when(userService.findById(1L)).thenReturn(user);
        
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("Carlos"));
    }
}

@DataJpaTest configura únicamente la capa de persistencia, proporcionando una base de datos en memoria para las pruebas:

@DataJpaTest
class ProductRepositoryTest {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Test
    void shouldSaveAndRetrieveProduct() {
        Product product = new Product("Laptop", 999.99);
        Product saved = productRepository.save(product);
        
        assertThat(saved.getId()).isNotNull();
        assertThat(saved.getName()).isEqualTo("Laptop");
    }
}

Configuración del entorno de testing

Spring Boot permite personalizar el entorno de testing mediante perfiles específicos y archivos de configuración dedicados. Esta flexibilidad facilita la creación de entornos de prueba que replican condiciones reales sin afectar los datos de producción.

El archivo application-test.properties permite definir configuraciones específicas para las pruebas:

spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=create-drop
logging.level.org.springframework.web=DEBUG

La anotación @ActiveProfiles activa perfiles específicos durante la ejecución de las pruebas:

@SpringBootTest
@ActiveProfiles("test")
class ServiceIntegrationTest {
    
    @Autowired
    private EmailService emailService;
    
    @Test
    void shouldSendEmailInTestMode() {
        // El servicio usará la configuración del perfil 'test'
        boolean sent = emailService.sendWelcomeEmail("test@email.com");
        assertThat(sent).isTrue();
    }
}

Mocking y stubbing

El mocking es una técnica esencial en las pruebas que permite simular el comportamiento de dependencias externas. Spring Test se integra con Mockito para proporcionar capacidades avanzadas de mocking directamente en el contexto de Spring.

@MockBean reemplaza beans del contexto de Spring con mocks, permitiendo controlar su comportamiento durante las pruebas:

@SpringBootTest
class OrderServiceTest {
    
    @MockBean
    private PaymentService paymentService;
    
    @MockBean
    private InventoryService inventoryService;
    
    @Autowired
    private OrderService orderService;
    
    @Test
    void shouldProcessOrderSuccessfully() {
        // Arrange
        when(inventoryService.isAvailable("PRODUCT_123")).thenReturn(true);
        when(paymentService.processPayment(100.0)).thenReturn(true);
        
        // Act
        Order result = orderService.createOrder("PRODUCT_123", 100.0);
        
        // Assert
        assertThat(result.getStatus()).isEqualTo(OrderStatus.CONFIRMED);
        verify(paymentService).processPayment(100.0);
        verify(inventoryService).reserveProduct("PRODUCT_123");
    }
}

TestContainers para pruebas realistas

TestContainers permite ejecutar pruebas con bases de datos reales en contenedores Docker, proporcionando un entorno de testing más cercano a producción. Spring Boot 3.1+ incluye soporte nativo para TestContainers.

@SpringBootTest
@Testcontainers
class DatabaseIntegrationTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");
    
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void shouldPersistUserInRealDatabase() {
        User user = new User("Maria", "maria@email.com");
        User saved = userRepository.save(user);
        
        assertThat(saved.getId()).isNotNull();
        assertThat(userRepository.count()).isEqualTo(1);
    }
}
Empezar curso de SpringBoot

Lecciones de este módulo de SpringBoot

Lecciones de programación del módulo Testing con Spring Test del curso de SpringBoot.

Ejercicios de programación en este módulo de SpringBoot

Evalúa tus conocimientos en Testing con Spring Test con ejercicios de programación Testing con Spring Test de tipo Test, Puzzle, Código y Proyecto con VSCode.