Pruebas Unitarias con Mockito en Entornos Spring Boot

Configuración de Mockito en Proyectos Maven

Para integrar Mockito en un proyecto Spring Boot, incluye la siguiente dependencia en el archivo pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

Plantilla Básica para Pruebas

Ejemplo de una clase de prueba que utiliza anotaciones de Mockito para simular dependencias:

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

public class ProductoServiceTest {
    
    @Mock
    private ProductoRepositorio productoRepo;
    
    @InjectMocks
    private ProductoServiceImpl productoService;
    
    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    
    @Test
    public void testObtenerProductoPorId() {
        // Definir comportamiento simulado del repositorio
        when(productoRepo.buscarPorId(10L))
           .thenReturn(new Producto("Monitor", 299.99));
        
        // Ejecutar el método bajo prueba
        Producto resultado = productoService.obtenerDetalle(10L);
        
        // Verificar resultados e interacciones
        assertNotNull(resultado);
        assertEquals("Monitor", resultado.getNombre());
        verify(productoRepo, times(1)).buscarPorId(10L);
    }
}

Operaciones Fundamentales de Mockito

Creación de Objetos Mock

Se pueden definir mocks mediante anotaciones o instancias manuales:

// Mediante anotación
@Mock
private MiServicio servicioMock;

// Creación programática
MiServicio mockManual = mock(MiServicio.class);

Stubbing de Métodos

Simula respuestas específicas para métodos dependientes:

// Retorno de valor predeterminado
when(mock.metodo()).thenReturn(valor);

// Simulación de excepciones
when(mock.metodo()).thenThrow(new RuntimeException());

// Respuestas secuenciales
when(mock.metodo())
  .thenReturn(primerValor)
  .thenReturn(segundoValor);

// Uso de matchers de parámetros
when(mock.metodo(anyInt(), startsWith("test"))).thenReturn(valor);

Verificación de Interacciones

Comprueba que los métodos mock se invocaron correctamente:

// Verificación básica de llamada
verify(mock).metodo();

// Conteo exacto de invocaciones
verify(mock, times(3)).metodo();
verify(mock, never()).metodo();

// Captura y validación de argumentos
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mock).metodo(captor.capture());
assertEquals("esperado", captor.getValue());

Escenarios Avanzados

Métodos Void

Controla el comportamiento de métodos sin valor de retorno:

doThrow(new IllegalStateException())
  .when(mock).metodoVoid();

doNothing().when(mock).metodoVoid();

Spy para Simulación Selectiva

Permite simular solo ciertos métodos de un objeto real:

List listaReal = new ArrayList();
List listaEspia = spy(listaReal);

// Mockear únicamente el método size
doReturn(50).when(listaEspia).size();

Verificación con Tiempo de Espera

Valida que un método sea llamado dentro de un intervalo específico:

verify(mock, timeout(150).atLeast(1)).metodo();

Recomendaciones de Uso

  • Utiliza @InjectMocks para la inyección automática de dependencias mockeadas.
  • Prefiere matchers de parámetros como any() o eq() en lugar de valores fijos para mayor flexibilidad.
  • Diseña cada caso de prueba para validar un único comportamiento o flujo.
  • Emplea la anotación @Captor para capturar argumentos en verificaciones complejas.

Ejemplo Integrado: Servicio de Citas Médicas

Implementación de pruebas para un servicio que gestiona citas médicas, con dependencias en repositorios y servicios externos.

public class CitaServiceTest {
    @Mock
    private MedicoRepositorio medicoRepo;
    
    @Mock
    private SistemaNotificaciones notificaciones;
    
    @InjectMocks
    private CitaServiceImpl citaService;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void deberiaAgendarCitaExitosamente() {
        // Configurar comportamientos simulados
        when(medicoRepo.estaDisponible("DR-123", "2023-11-15")).thenReturn(true);
        when(notificaciones.enviarConfirmacion(anyString())).thenReturn(true);

        // Ejecutar la lógica de negocio
        Cita cita = new Cita("DR-123", "2023-11-15", "10:00");
        ResultadoCita resultado = citaService.agendar(cita);

        // Validar resultados y interacciones
        assertTrue(resultado.isConfirmada());
        verify(notificaciones).enviarConfirmacion(eq("DR-123"));
        verify(medicoRepo).bloquearHorario("DR-123", "2023-11-15", "10:00");
    }
}

Este caso ilustra cómo simular dependencias externas y verificar el flujo completo del servicio, asegurando tanto la lógica de negocio como las interacciones con componentes secundarios.

Etiquetas: Mockito Spring Boot JUnit 5 Mock Objects dependency injection

Publicado el 7-9 08:10