Pruebas en Java: Unit, Integration, E2E a nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Pruebas en Java: Unit, Integration, E2E — Nivel Avanzado



En el desarrollo moderno de Java, las pruebas no son solo una formalidad, sino una práctica fundamental que garantiza la estabilidad y previsibilidad del código. Las pruebas básicas, como simples @Test con Assert.assertEquals, han dado paso a estrategias complejas de múltiples capas. En este artículo, analizaremos técnicas avanzadas para cada nivel de la pirámide de pruebas: Unit, Integration y End-to-End (E2E). Aprenderá a escribir pruebas mantenibles, rápidas y confiables que convertirán su código en un mecanismo bien ajustado.



1. Pruebas Unit avanzadas: Mocks, Stubs y Parametrización



Las pruebas Unit verifican una pieza de código aislada (generalmente una clase o método). A nivel avanzado, pasamos de escenarios simples a interacciones complejas y casos límite. La herramienta clave aquí es la librería Mockito.



1.1. Uso de ArgumentCaptor y respuestas complejas (Answer)



A menudo es necesario no solo verificar que se llamó a un método, sino también qué argumentos se le pasaron. Para esto se utiliza ArgumentCaptor.



// Ejemplo: Verificar que se envió el mensaje correcto al servicio@ExtendWith(MockitoExtension.class)public class NotificationServiceTest {    @Mock    private EmailSender emailSender;    @InjectMocks    private NotificationService notificationService;

@Test void shouldSendEmailWithCorrectContent() { // Arrange User user = new User("test@example.com", "Test User"); // Act notificationService.sendWelcomeEmail(user); // Assert ArgumentCaptor<EmailMessage> captor = ArgumentCaptor.forClass(EmailMessage.class); verify(emailSender).send(captor.capture()); EmailMessage capturedMessage = captor.getValue(); assertEquals("test@example.com", capturedMessage.getTo()); assertTrue(capturedMessage.getBody().contains("Test User")); }}


Para crear respuestas dinámicas basadas en los datos de entrada, use la interfaz Answer:



@Testvoid testDynamicResponse() {    when(dataRepository.findById(anyLong())).thenAnswer(invocation -> {        Long id = invocation.getArgument(0);        return new DataEntity(id, "Entity with ID: " + id);    });    assertEquals("Entity with ID: 5", dataService.getDataById(5L).getName());}


1.2. Pruebas parametrizadas (JUnit 5)



La parametrización permite ejecutar la misma prueba con diferentes conjuntos de datos, evitando la duplicación de código. JUnit 5 proporciona anotaciones potentes como @CsvSource y @MethodSource.



@ParameterizedTest@CsvSource({    "10, 20, 30",    "0, 0, 0",    "-5, 5, 0"})void testSum(int a, int b, int expectedSum) {    Calculator calculator = new Calculator();    assertEquals(expectedSum, calculator.add(a, b));}


2. Pruebas de Integración: Trabajo con bases de datos y servicios externos



Las pruebas de integración verifican la interacción entre componentes del sistema, por ejemplo, con una base de datos real o colas de mensajes. El principal desafío es la gestión del estado del entorno. Aquí es donde Testcontainers viene al rescate.



2.1. Testcontainers para una base de datos aislada



Testcontainers levanta contenedores Docker con el software necesario (PostgreSQL, Redis, Kafka) durante las pruebas, garantizando limpieza y reproducibilidad.



@SpringBootTest@Testcontainersclass UserRepositoryIntegrationTest {

@Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15") .withDatabaseName("testdb") .withUsername("test") .withPassword("test");

@DynamicPropertySource static void configureProperties(DynamicPropertyR

Blogs

Book Recommendations