Ir al contenido

Pruebas de lógica de negocio

@DataJpaTest
@Transactional
@Import(CompanyService.class)
public class CompanyServiceTest {
@Autowired
private CompanyService companyService;
@Autowired
private TestEntityManager entityManager;
private PodamFactory factory = new PodamFactoryImpl();
private List<CompanyEntity> companyList = new ArrayList<>();
}
  • @DataJpaTest carga la configuración necesaria para JPA, sin servidor web.
  • @Transactional ejecuta cada prueba en una transacción que se revierte al terminar.
  • @Import(CompanyService.class) incorpora al contexto el servicio bajo prueba.
  • @Autowired inyecta el servicio y el TestEntityManager.

Permite preparar y verificar la base de datos de pruebas sin pasar por el servicio que se evalúa:

  • persist(entity) inserta datos.
  • find(Class, id) busca una entidad.
  • createQuery(...) ejecuta consultas.
  • la limpieza entre pruebas garantiza aislamiento.

El servicio usa su repository; la prueba usa TestEntityManager directamente para que un error en el servicio no contamine la preparación de datos.

@BeforeEach ejecuta setUp() antes de cada test. JUnit no garantiza un orden, por lo que cada prueba debe iniciar con un estado conocido.

  1. @BeforeEach: limpiar la base e insertar datos frescos.
  2. @Test: ejecutar el escenario con esos datos.
  3. @AfterEach, si existe: ejecutar tareas posteriores. Normalmente el rollback de @Transactional basta.
  4. Repetir el ciclo para cada método de prueba.

Explora el ciclo y los escenarios de una prueba

Compara la preparación común con un caso feliz y una regla violada.

@BeforeEachPaso 1 de 4
Limpiar datos

clearData elimina el estado anterior mediante una consulta JPQL.

delete from CompanyEntity
@BeforeEach
void setUp() {
clearData();
insertData();
}
private void clearData() {
entityManager.getEntityManager()
.createQuery("delete from CompanyEntity")
.executeUpdate();
}
private void insertData() {
for (int i = 0; i < 3; i++) {
CompanyEntity entity =
factory.manufacturePojo(CompanyEntity.class);
entityManager.persist(entity);
companyList.add(entity);
}
}

La consulta de limpieza es JPQL, no SQL nativo. Si hay cinco métodos @Test, setUp() se ejecuta cinco veces.

Podam genera objetos Java con datos aleatorios para reducir la preparación manual:

CompanyEntity entity =
factory.manufacturePojo(CompanyEntity.class);

En relaciones bidireccionales puede producir un ciclo infinito al intentar construir ambos lados repetidamente. @PodamExclude excluye de la generación el atributo que cierra el ciclo. Después se asignan manualmente las relaciones que el escenario necesita.

Buenas prácticas: usar una sola fábrica por clase, modificar explícitamente los datos relevantes para el caso, excluir relaciones problemáticas y persistir las entidades en el orden que exigen sus dependencias.

Un escenario positivo verifica el caso feliz:

@Test
void testCreateCompany() throws EntityNotFoundException,
IllegalOperationException {
CompanyEntity newEntity =
factory.manufacturePojo(CompanyEntity.class);
CompanyEntity result = companyService.createCompany(newEntity);
assertNotNull(result);
CompanyEntity entity = entityManager.find(
CompanyEntity.class, result.getId());
assertEquals(newEntity.getName(), entity.getName());
}

Un escenario negativo verifica una regla y la excepción esperada:

@Test
void testCreateInvalidCompany() {
assertThrows(IllegalOperationException.class, () -> {
CompanyEntity newEntity =
factory.manufacturePojo(CompanyEntity.class);
newEntity.setName(companyList.get(0).getName());
companyService.createCompany(newEntity);
});
}

La estrategia mínima es un caso positivo y un caso negativo por cada regla de negocio. Si un método valida tres reglas, requiere al menos cuatro pruebas.

1. ¿Qué carga @DataJpaTest?
2. ¿Por qué se usa @BeforeEach?
3. ¿Por qué preparar datos con TestEntityManager y no con el servicio?
4. ¿Qué verifica assertThrows(IllegalOperationException.class, ...)?
5. Si hay tres reglas de negocio, ¿cuál es la cobertura mínima propuesta?
6. ¿Para qué se usa @PodamExclude?