JUnit 5 Unit test for JPAQuery

Viewed 675

I have been trying to find an approach to write a simple unit test case for the below function , but not able to figure out anything . The function basically uses JPA Query on a QueryDSL Q class and returns the result as a Map. Any sample code would suffice . How do I mock the entire JPA Query to return a Map .

@Autowired
EntityManager entityManager;
public Map<Integer, Double> getUsersCategoryAppliedBudgetMap(int opportunityTypeId, LocalDate startDate,
            LocalDate endDate) {
        QApplications applicationPath = QApplications.applications;
        JPAQuery<ApplicationApprovals> query = new JPAQuery<>(entityManager);
        log.info("Executing Budget Query for opportunity type" + opportunityTypeId);
        return query.from(applicationPath).where(applicationPath.applicationStatus.id
                .in(PENDING_APPROVAL.getId(), SENT_BACK.getId(), APPROVED.getId(), PENDING_WITHDRAW_APPROVAL.getId(),
                        PENDING_COMPLETION_DOCUMENT.getId(), REVIEW_COMPLETION_DOCUMENT.getId(), COMPLETED.getId())
                .and(applicationPath.opportunity.opportunityTypes.id.eq(opportunityTypeId))
                .and(applicationPath.appliedDate.between(startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX))))
                .groupBy(applicationPath.user.id)
                .transform(GroupBy.groupBy(applicationPath.user.id).as(applicationPath.cost.sum()));
    }
3 Answers

If you are using spring-boot and it's starters and leveraging Spring Data JPA repositories then you could try using @DataJpaTest to setup an In-memory H2 instance an run your queries against that to check your code. Doc.

Are you sure you want a unit test, i. e. a test that's mocking your persistency layer? What exactly would you want to verify by that test?

[Edit: How did this end up as an answer rather than being a comment?]

Rather than having code which calls new JPAQuery<>(entityManager); you could wire in an interface which supplies the JPAQuery. You can then mock this interface, so the JPAQuery supplied is a mock, controlled by your test.

here is an example how the interface supplies the new object:

@RequiredArgsConstructor
public static class NewObject {

  private final Map<String, Integer> entries; //Equivalent of EntityManager in example above
  private final Function<Map<String, Integer>, Map<String, Integer>> mapSupplier; // new dependency, which supplies the new object to be mocked

  public Integer getKeyForValue(String key) {
    //rather than calling myMap = new HashMap<>(entries) use the method call
    //equivalent replacement of: JPAQuery<ApplicationApprovals> query = new JPAQuery<>(entityManager)
    Map<String, Integer> myMap = mapSupplier.apply(entries);
    return myMap.get(key);
  }
}

then in your test you can insert an alternative implementation of the interface, allowing you to provide a mock of the object you want to control:

@Test
public void valueReturnedFromNewObjectIsFromSuppliedMap() {

  //create a mock which will provide your new object
  Function<Map<String, Integer>, Map<String, Integer>> mockMapSupplier = mock(Function.class);

  //set up the object you want the mock to return
  Map<String, Integer> otherEntries = new HashMap<>();
  Integer alternativeValue = 1000;
  otherEntries.put(KEY, alternativeValue);

  //using Mockito, tell the mock when to return your expected response
  when(mockMapSupplier.apply(entries)).thenReturn(otherEntries);

  //create the class you want to test using your mock
  NewObject newObject = new NewObject(entries, mockMapSupplier);

  //verify it behaves as expected
  assertThat(newObject.getKeyForValue(KEY), is(alternativeValue));
}

The examples above use Mockito (https://site.mockito.org) to provide mock objects which can be controlled in the unit test. If you're not familiar with Mockito I recommend looking at the documentation and examples they provide to get an understanding of what it does and how you can use it in tests to verify the behaviour of your code.

Related