How to test JPA Specification
Asked Answered
R

2

8

I have a builder that create a JPA Specification according to filters. How do I test the builder ? assertEquals method always return false when comparing Specifications...

I already try to call Specification::toPredicate method with mock args but the returned value is null. I would like to avoid loading the entityManager.

Specification<MyClass> nullSpecification = Specification.where(null);
Specification<MyClass> nullSpecification2 = Specification.where(null);
assertEquals(nullSpecification, nullSpecification2);

I expected assertEquals(nullSpecification, nullSpecification2) to return true but the actual value is false. How can I compare JPA Specification ?

Rhee answered 12/9, 2019 at 13:21 Comment(0)
S
7

I see the following options.

  1. Bite the bullet and create an integration test, i.e. create an EntityManager and run queries against an in memory database or Testcontainers.

  2. Inspect the internals of the generated Predicate by passing in carefully configured mocks and then using type checking, reflection and knowledge of internals of your JPA provider to assert that you'll get the right predicate.

The problem with the 2nd approach is that it is very brittle and is likely to break although the Specification is actually fine. I therefore would go with option 1 although I'd prefer a unit test as well.

Sequester answered 13/9, 2019 at 5:46 Comment(1)
Thank you, I think I will do both :)Rhee
E
0

The only way I could find to unit-test a Specification is by triggering the toPredicate() method within your test. This is because in your production code, you usually are the one who provides the implementation for the toSpecification() method, usually as a lambda expression.

Example:

// The method with the specification
public Specification<Entity> mySpecificationMethod() {

  Specification<Entity> specification = (root, query, criteriaBuilder) -> {
    List<Predicate> predicates = new ArrayList<>();
   
    for (Filter filter: filters) {
       predicates.add(createPredicate(root, criteriaBuilder, filter));
    }

    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
  }
  
  return specification;
}

// The test class
class TestSpecification {

 @Mock
 private CriteriaQuery<OutletEntity> criteriaQuery;

 @Mock
 private CriteriaBuilder criteriaBuilder;

 @Mock
 private Root<OutletEntity> root;


 void testMySpecification() {
   Specification<Entity> spec = mySpecificationMethod();

   // Manually call toPredicate() so that the lambda expression in your production code can be triggered
   specification.toPredicate(root, criteriaQuery, criteriaBuilder);

   // assert that criteriaBuilder.and() was called
   verify(criteriaBuilder).and(any());
 }
}
Exploration answered 16/8, 2024 at 7:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.