Tabla de Contenidos

05 - Pruebas unitarias: JUnit y Mockito

Una prueba unitaria comprueba una unidad de código — normalmente un método o una clase — aislada de sus dependencias. JUnit 5 es el framework estándar en Java para escribirlas y Mockito permite simular las dependencias.

1. Estructura de una prueba

Las pruebas siguen el patrón AAA (*Arrange-Act-Assert*): preparar, actuar, comprobar.

@Test
void calculatesTotalWithDiscount() {
    // Arrange
    PriceCalculator calculator = new PriceCalculator();

    // Act
    Money total = calculator.totalWithDiscount(100, 20);

    // Assert
    assertEquals(80, total.amount());
}

Anotación Función
@Test Marca un método como prueba
@DisplayName Descripción legible de la prueba
@BeforeEach Se ejecuta antes de cada prueba
@AfterEach Se ejecuta después de cada prueba
@Disabled Desactiva temporalmente la prueba

Con Maven, mvn test compila y ejecuta todas las pruebas.

2. Aserciones

Las aserciones comparan el resultado esperado con el real. Si fallan, la prueba falla y muestra la diferencia.

assertEquals(expected, actual);
assertTrue(condition);
assertFalse(condition);
assertNull(value);
assertThrows(IllegalArgumentException.class, () -> calculator.totalWithDiscount(-1, 0));

3. Ciclo de vida

class CalculatorTest {
    private PriceCalculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new PriceCalculator();
    }

    @Test
    void calculatesTotal() { /* ... */ }
}

@BeforeEach crea un estado limpio para cada prueba: ninguna depende de la anterior.

4. Mockito: dobles de prueba

Una unidad aislada no puede tocar la base de datos ni la red. Un doble de prueba sustituye una dependencia por una simulación controlada. Con Mockito se declara el mock, se configura su comportamiento y se comprueba su uso.

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock
    OrderRepository repository;

    @InjectMocks
    OrderService service;

    @Test
    void returnsOrderWhenFound() {
        Order stored = new Order(42L);
        when(repository.findById(42L)).thenReturn(Optional.of(stored));

        Order result = service.findById(42L);

        assertEquals(stored, result);
        verify(repository).findById(42L);
    }
}

Herramienta Función
@Mock Crea el doble
when(…).thenReturn(…), given(…) Define qué devuelve el doble
@InjectMocks Inyecta los dobles en el objeto probado
verify(…) Comprueba que se llamó al doble como se esperaba

5. Integración entre capas

En un monolito por capas, las pruebas unitarias también cubren la colaboración entre capas, simulando la capa inferior. Por ejemplo, probar el controller con el service mockeado comprueba que el controlador delega y traduce correctamente, sin ejecutar la lógica real:

@ExtendWith(MockitoExtension.class)
class BookControllerTest {
    @Mock
    BookService service;

    @Test
    void mapsServiceResultToResponse() {
        when(service.findById(1L)).thenReturn(new Book(1L, "Clean Code"));

        BookResponse response = new BookController(service).get(1L);

        assertEquals("Clean Code", response.title());
    }
}

Así se comprueba el contrato entre capas: qué se pide y qué se devuelve, no la implementación de cada capa.

Las unitarias no tocan la base de datos ni la red. Si una prueba necesita infraestructura real, ya no es unitaria: se prueba en el nivel de integración o E2E según corresponda.