Pruebas en Java: unit, integration, e2e de la A a la Z

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

Introducción: Por qué las pruebas son la base del código Java de calidad

Cada desarrollador Java se ha enfrentado a la situación en la que, después de corregir un error, aparecen tres nuevos. Para romper este círculo vicioso, existen las pruebas. No es solo una marca en una lista de verificación, sino una disciplina profesional que ahorra nervios, dinero y la reputación del producto.

En este artículo, analizaremos tres niveles clave de pruebas: unit (pruebas unitarias), integration (pruebas de integración) y e2e (end-to-end). Los veremos con ejemplos prácticos en Java, utilizando frameworks populares: JUnit 5, Mockito, Spring Boot Test y Selenium. Aprenderás cómo construir la pirámide de pruebas correcta y no sobrecargar el proyecto con pruebas inútiles.

1. Pruebas Unitarias: Verificación de unidades atómicas de código

Las pruebas unitarias son el nivel más bajo y el más rápido. Verifican el trabajo de una sola "unidad" (generalmente un método o clase) de forma aislada de las dependencias externas (BD, API, sistema de archivos).

1.1 Herramientas y frameworks

  • JUnit 5 — el estándar de facto para escribir pruebas en Java.
  • Mockito — biblioteca para crear mocks y stubs.
  • AssertJ — biblioteca para verificaciones fluidas (afirmaciones).

1.2 Ejemplo de prueba unitaria con JUnit 5 y Mockito

Imaginemos que tenemos un servicio OrderService que depende de un repositorio PaymentRepository. Queremos probar la lógica del servicio sin llamar a la BD real.

// Clase a probar
public class OrderService {
    private final PaymentRepository paymentRepository;

    public OrderService(PaymentRepository paymentRepository) {
        this.paymentRepository = paymentRepository;
    }

    public String processOrder(Order order) {
        if (order.getAmount() <= 0) {
            throw new IllegalArgumentException("El monto del pedido debe ser positivo");
        }
        boolean success = paymentRepository.pay(order);
        return success ? "Pago exitoso" : "Error de pago";
    }
}

// Prueba unitaria
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private PaymentRepository paymentRepository;

    @InjectMocks
    private OrderService orderService;

    @Test
    void testProcessOrder_Success() {
        // Arrange
        Order order = new Order(100.0);
        when(paymentRepository.pay(order)).thenReturn(true);

        // Act
        String result = orderService.processOrder(order);

        // Assert
        assertEquals("Pago exitoso", result);
        verify(paymentRepository, times(1)).pay(order);
    }

    @Test
    void testProcessOrder_NegativeAmount() {
        // Arrange
        Order order = new Order(-10.0);

        // Act & Assert
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.processOrder(order);
        });
    }
}

Mejores prácticas para pruebas unitarias:

  • Una prueba, una verificación (principio FIRST: Fast, Isolated, Repeatable, Self-validating, Timely).
  • Usa nombres descriptivos: shouldReturnSuccess_whenPaymentIsApproved.
  • No pruebes métodos privados — prueba el contrato público.
  • Cubre casos límite: null, cadenas vacías, números negativos.

2. Pruebas de Integración: Verificación de la interacción de componentes

Las pruebas de integración verifican cómo los diferentes módulos del sistema

Blogs

Book Recommendations