balteo
balteo

Reputation: 24669

How to unit test a jpa repository method?

I have coded a JPA repository method and I am now realizing it is impossible to unit test.

Can anyone please advise how to unit test the following method or how to refactor my repository so that it is unit-testable?

Here is the problematic method:

@Override
public List<Pli> findPlisByMultiField(String identifiant, Date dateReceptionFrom, Date dateReceptionTo, PaiementEnum paiement, AREnum ar, String numeroAR, FDVEnum FDV, ConteneurNum conteneurNum, StatutPli statut) {
    log.debug("findPlisByMultiField");

    CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
    CriteriaQuery<Pli> c = criteriaBuilder.createQuery(Pli.class);
    Root<Pli> pli = c.from(Pli.class);

    List<Predicate> criteria = new ArrayList<Predicate>();

    if (identifiant != null && !identifiant.trim().equals("")) {
        ParameterExpression<String> parameterIdentifiant = criteriaBuilder.parameter(String.class, "identifiant");
        Predicate conditionIdentifiant = criteriaBuilder.like(pli.<String> get("identifiant"), parameterIdentifiant);
        criteria.add(conditionIdentifiant);
    }

    if (dateReceptionFrom != null && dateReceptionTo != null) {
        ParameterExpression<Date> parameterDateReceptionFrom = criteriaBuilder.parameter(Date.class, "dateReceptionFrom");
        ParameterExpression<Date> parameterDateReceptionTo = criteriaBuilder.parameter(Date.class, "dateReceptionTo");
        Predicate conditionDateReception = criteriaBuilder.between(pli.<Date> get("dateReception"), parameterDateReceptionFrom, parameterDateReceptionTo);
        criteria.add(conditionDateReception);
    }

    if (paiement != null) {
        if (paiement.equals(PaiementEnum.IsPaiement)) {
            Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), true);
            criteria.add(conditionPaiementEnum);
        } else {
            Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), false);
            criteria.add(conditionPaiementEnum);
        }
    }

    if (ar != null) {
        if (ar.equals(AREnum.IsAR)) {
            Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), true);
            criteria.add(conditionAREnum);
        } else {
            Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), false);
            criteria.add(conditionAREnum);
        }
    }

    if (numeroAR != null && !numeroAR.trim().equals("")) {
        ParameterExpression<String> parameterNumeroAR = criteriaBuilder.parameter(String.class, "numeroAR");
        Predicate conditionNumeroAR = criteriaBuilder.like(pli.<String> get("numeroAR"), parameterNumeroAR);
        criteria.add(conditionNumeroAR);
    }

    if (FDV != null) {
        if (FDV.equals(FDVEnum.IsFDV)) {
            Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), true);
            criteria.add(conditionFDVEnum);
        } else {
            Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), false);
            criteria.add(conditionFDVEnum);
        }
    }

    if (conteneurNum != null) {
        ParameterExpression<ConteneurNum> parameterConteneurNum = criteriaBuilder.parameter(ConteneurNum.class, "conteneurNum");
        Predicate conditionConteneurNum = criteriaBuilder.equal(pli.<ConteneurNum> get("conteneurNum"), parameterConteneurNum);
        criteria.add(conditionConteneurNum);
    }

    if (statut != null) {
        ParameterExpression<StatutPli> parameterStatut = criteriaBuilder.parameter(StatutPli.class, "statut");
        Predicate conditionStatut = criteriaBuilder.equal(pli.<StatutPli> get("statut"), parameterStatut);
        criteria.add(conditionStatut);
    }

    if (criteria.size() == 0) {
        return Pli.findAllPlis();
    } else if (criteria.size() == 1) {
        c.where(criteria.get(0));
    } else {
        c.where(criteriaBuilder.and(criteria.toArray(new Predicate[0])));
    }

    TypedQuery<Pli> q = em.createQuery(c);
    if (identifiant != null && !identifiant.trim().equals("")) {
        q.setParameter("identifiant", "%" + identifiant + "%");
    }
    if (dateReceptionFrom != null && dateReceptionTo != null) {
        q.setParameter("dateReceptionFrom", dateReceptionFrom);
        q.setParameter("dateReceptionTo", dateReceptionTo);
    }

    if (numeroAR != null && !numeroAR.trim().equals("")) {
        q.setParameter("numeroAR", "%" + numeroAR + "%");
    }

    if (conteneurNum != null) {
        q.setParameter("conteneurNum", conteneurNum);
    }

    if (statut != null) {
        q.setParameter("statut", statut);
    }

    return q.getResultList();
}

Upvotes: 4

Views: 1439

Answers (2)

julaine
julaine

Reputation: 1614

H2-Tests are quick, but they have the disadvantage that you are testing against a different SQL-implementation than the one you use in production, and your database-schema doesn't exist in your test (meaning mismatches between query/entity and the real schema will no be detected)

Fortunately, we now have Testcontainers which spins up a database for your integration-testing. With the spring @Transactional-annotation you can make tests isolated without requiring a new database for each test or relying on manual cleanup.


SETUP: I am assuming a few things: version numbers, database, xml-pom, but you can easily generalize.

Define an application-integrationtest.properties (where you would normally define spring-profiles)

spring.datasource.url=jdbc:tc:postgresql:13.3:///your-project-name
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver

add the dependency to testcontainers, for your maven pom.xml that would be:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.8</version>
    <scope>test</scope>
</dependency>

Then write your test like this:


@DataJpaTest // or @SpringBootTest
@ActiveProfiles("integrationtest")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class DatabaseTest {
    // can autowire repositories here, which will hit real DB.
}

Upvotes: 1

Frank Orellana
Frank Orellana

Reputation: 1898

Well, I don't think you will be able to Unit Test it as it's strictly specified, but you can make a test using an in memory database (look out for HSQL) so that the app don't need to actually go to the real database just for testing.

That way you will be able to create an automated test that could run inside JUnit for example, mocking maybe only some of the methods.

Upvotes: 3

Related Questions