CodeGym /Cursos /Módulo 5. Spring /Lección 135: Pruebas de servicios y repositorios

Lección 135: Pruebas de servicios y repositorios

Módulo 5. Spring
Nivel 9 , Lección 4
Disponible

Los servicios son esas partes de tu código donde vive la lógica de negocio. Si los controladores son la fachada de tu aplicación, los servicios son el "cerebro". Los errores en los servicios pueden llevar a la ejecución incorrecta de los procesos de negocio.

Probar los servicios ayuda a:

  • Comprobar que la lógica de la aplicación funciona como debería.
  • Asegurarse de que todas las llamadas a métodos e interacciones con dependencias se realizan correctamente.
  • Detectar bugs antes de compilar y desplegar la aplicación.

Mockito como tu mejor amigo

Para probar los servicios vamos a usar activamente Mockito. ¿Por qué? Porque necesitamos testear la lógica de negocio aislándola de todas las dependencias externas, como bases de datos o APIs externas.

Pasos principales para testear servicios

  1. Crear mocks para todas las dependencias del servicio (repositorios, otros servicios, etc.).
  2. Configurar el comportamiento de los mocks usando when() y thenReturn().
  3. Comprobar que los métodos del servicio provocan las acciones esperadas y devuelven resultados correctos.
  4. Opcionalmente, verificar que las dependencias se llamen el número de veces esperado (o que no se llamen), usando métodos como verify().

Práctica: pruebas de servicios

Tomemos un ejemplo. Tenemos un servicio que calcula descuentos para usuarios. Accede al repositorio para obtener información del usuario y otra lógica de la aplicación.

Servicio


@Service
public class DiscountService {

    private final UserRepository userRepository;

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

    public double calculateDiscount(Long userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("User not found"));

        if (user.isPremium()) {
            return 20.0; // 20% de descuento para usuarios premium
        }
        return 5.0; // 5% de descuento para el resto
    }
}

Pruebas

1. Creamos la clase de test y configuramos los mocks:


@ExtendWith(MockitoExtension.class)
class DiscountServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private DiscountService discountService;

    // Datos de prueba
    private final User premiumUser = new User(1L, "PremiumUser", true);
    private final User regularUser = new User(2L, "RegularUser", false);

    // ...
}

2. Probamos el método calculateDiscount() para usuarios premium:


@Test
void shouldReturn20PercentDiscountForPremiumUsers() {
    // Configuramos el mock
    when(userRepository.findById(1L)).thenReturn(Optional.of(premiumUser));

    // Acción
    double discount = discountService.calculateDiscount(1L);

    // Comprobación
    assertEquals(20.0, discount);
    verify(userRepository, times(1)).findById(1L); // Aseguramos que el repositorio se llamó exactamente 1 vez
}

3. Probamos el método calculateDiscount() para usuarios normales:


@Test
void shouldReturn5PercentDiscountForRegularUsers() {
    // Configuramos el mock
    when(userRepository.findById(2L)).thenReturn(Optional.of(regularUser));

    // Acción
    double discount = discountService.calculateDiscount(2L);

    // Comprobación
    assertEquals(5.0, discount);
    verify(userRepository, times(1)).findById(2L);
}

4. Manejamos el caso cuando el usuario no se encuentra:


@Test
void shouldThrowExceptionWhenUserNotFound() {
    // Configuramos el mock
    when(userRepository.findById(anyLong())).thenReturn(Optional.empty());

    // Comprobación de la excepción
    Exception exception = assertThrows(IllegalArgumentException.class,
        () -> discountService.calculateDiscount(99L));

    assertEquals("User not found", exception.getMessage());
    verify(userRepository, times(1)).findById(99L);
}

Ahora estamos seguros de que nuestro servicio funciona correctamente y responde ante todos los escenarios posibles.


Introducción a las pruebas de repositorios

Los repositorios son el puente entre tu aplicación y la base de datos. Por eso es importante asegurarse de que interactúan correctamente con la base, ejecutando consultas y procesamientos adecuados.

¿Cómo testear repositorios?

  1. Usar una base de datos embebida, por ejemplo, H2 (en memoria) para los tests.
  2. La anotación @DataJpaTest ayuda a configurar automáticamente el entorno de pruebas para trabajar con repositorios JPA.
  3. Verificar que los resultados de las consultas coinciden con lo esperado.

Práctica: Pruebas de repositorios

Entidad


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private boolean isPremium;

    // Constructores, getters y setters
}

Repositorio


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findAllByIsPremium(boolean isPremium);
}

Pruebas

1. Creamos la clase de test:


@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository userRepository;

    // ...
}

2. Probamos el método findAllByIsPremium:


@Test
void shouldFindAllPremiumUsers() {
    // Preparación de datos de prueba
    User premiumUser = new User(null, "PremiumUser", true);
    User regularUser = new User(null, "RegularUser", false);

    entityManager.persist(premiumUser);
    entityManager.persist(regularUser);
    entityManager.flush();

    // Acción
    List<User> result = userRepository.findAllByIsPremium(true);

    // Comprobación
    assertEquals(1, result.size());
    assertEquals("PremiumUser", result.get(0).getName());
}

3. Probamos los métodos estándar de JPA (por ejemplo, findById):


@Test
void shouldFindUserById() {
    // Preparación de datos
    User user = new User(null, "TestUser", false);
    User savedUser = entityManager.persistFlushFind(user);

    // Comprobación
    Optional<User> result = userRepository.findById(savedUser.getId());
    assertTrue(result.isPresent());
    assertEquals("TestUser", result.get().getName());
}

Conclusiones

Probar los servicios con Mockito aísla la lógica de las dependencias externas, permitiéndote enfocarte sólo en verificar la lógica de negocio. Mientras que testear los repositorios con la anotación @DataJpaTest y la base H2 ayuda a garantizar la corrección de las consultas y la manipulación de datos.

Has preparado con éxito tu base de código para tareas reales, y los bugs ya no podrán esconderse en tu aplicación. Ahora puedes avanzar a escenarios de testing más complejos, que verás en las siguientes lecciones.

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