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.