CodeGym /Cursos /Módulo 5. Spring /Lección 158: Pruebas de la aplicación: Unit y pruebas de ...

Lección 158: Pruebas de la aplicación: Unit y pruebas de integración

Módulo 5. Spring
Nivel 25 , Lección 7
Disponible

Vamos a escribir dos tipos de tests:

  1. Unit-tests: verifican métodos y componentes aislados. Nada de trabajar con base de datos o servicios externos. Usamos mocks (Mockito) para simular dependencias.
  2. Pruebas de integración: verifican el trabajo de varios componentes juntos. Por ejemplo, la interacción de controllers con services y la base de datos.

Preparación para las pruebas

Dependencias para testing

Spring Boot ya incluye todo lo necesario para testing básico. Sin embargo, conviene asegurarse de que en tu pom.xml estén añadidas estas dependencias:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <scope>test</scope>
</dependency>

Si usas Gradle, las líneas equivalentes se verían así:


testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.testcontainers:testcontainers'

Después de añadir las dependencias, asegúrate de que el proyecto se recarga correctamente.


Unit-tests

Empezamos escribiendo Unit-tests para la capa de servicios.

Ejemplo de código: test del servicio

Supongamos que tenemos la siguiente lógica de negocio en nuestro servicio:


@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User findUserById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
    }
}

Testear usando Mockito

Para probar esta clase necesitamos aislarla de la base de datos. Esto se consigue con mocks:


@ExtendWith(MockitoExtension.class) // Activamos la integración con Mockito
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void shouldReturnUserWhenFound() {
        // Arrange
        Long userId = 1L;
        User mockUser = new User(userId, "John Doe", "john.doe@example.com");
        when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser)); // Mockeamos el comportamiento del repositorio

        // Act
        User result = userService.findUserById(userId);

        // Assert
        assertNotNull(result);
        assertEquals("John Doe", result.getName());
        verify(userRepository, times(1)).findById(userId); // Verificamos que el método se llamó exactamente una vez
    }

    @Test
    void shouldThrowExceptionWhenUserNotFound() {
        // Arrange
        Long userId = 999L;
        when(userRepository.findById(userId)).thenReturn(Optional.empty());

        // Act & Assert
        assertThrows(UserNotFoundException.class, () -> userService.findUserById(userId));
    }
}

¿Qué ocurre aquí?

  1. Usamos la anotación @Mock para crear un UserRepository "falso".
  2. Con @InjectMocks inyectamos ese mock en UserService.
  3. En los tests configuramos el comportamiento del repositorio (when(...).thenReturn(...)) para crear un entorno controlado.
  4. Nos aseguramos de que el método funciona correctamente y lanza la excepción si el usuario no se encuentra.

Pruebas de integración

Ahora vamos a casos más complejos, cuando necesitamos comprobar la interacción de componentes. Usaremos una base de datos real (por ejemplo, H2) y probaremos el funcionamiento de controllers y services.

Configuración de pruebas de integración

Para las pruebas de integración de controllers, Spring Boot ofrece la anotación @SpringBootTest. Levanta el contexto de la aplicación y permite probar la interacción entre capas.

Ejemplo de REST controller a probar:


@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.findUserById(id);
        return ResponseEntity.ok(user);
    }
}

Test de integración del controller

Escribimos un test que hace llamadas directas a nuestra API:


@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // Ejecutamos con servidor web
@AutoConfigureMockMvc // Configuramos MockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldReturnUserWhenExists() throws Exception {
        mockMvc.perform(get("/api/users/1"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.name").value("John Doe"));
    }

    @Test
    void shouldReturnNotFoundWhenUserDoesNotExist() throws Exception {
        mockMvc.perform(get("/api/users/999"))
               .andExpect(status().isNotFound());
    }
}

Depuración de tests y errores típicos

  1. Error de configuración del contexto: asegúrate de que tu configuración de @SpringBootTest y los mocks estén correctamente configurados.
  2. Carga perezosa de datos: a veces las pruebas de integración pueden fallar porque las relaciones entre entidades se cargan de forma lazy. Esto se soluciona configurando carga explícita (FetchType.EAGER) o usando DTOs.
  3. Tests lentos: si tus tests son lentos, considera usar Testcontainers para aislar los tests localmente en contenedores.

Automatización de las pruebas

Integra los tests en el pipeline CI/CD. En cada nuevo commit tus Unit tests y pruebas de integración deben ejecutarse para evitar que bugs lleguen a la rama principal. La mayoría de herramientas (Jenkins, GitLab CI, GitHub Actions) se integran fácilmente con Maven o Gradle.


# Ejemplo de pipeline de GitLab CI
stages:
  - test

test:
  script:
    - ./mvnw test

Así puedes tener confianza en tu aplicación antes de desplegarla. Ahora sabes cómo escribir tanto Unit tests como pruebas de integración. ¿Listo para el siguiente paso? ¡Es hora de meterse en containerización y automatizar el build! 🚀

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION