1

I currently design an API using spring boot. In my service layer, I use Entity Manager for accessing the database. I have provided a method in my service layer below as an example.

public Object getFirstReturnDate(Long vehicle_id, LocalDateTime reservation_date){
    String string = "SELECT r.reservation_time from Reservation r left join fetch r.driverAssignment  where r.reservation_time > :reservation_time " +
            "and r.reserved_status.slug in ('pending','approved') and r.reserved_vehicle.id=:vehicle_id " +
            " order by r.reservation_time asc ";
    Query  query = em.createQuery(string);
    query.setParameter("reservation_time",reservation_date);
    query.setParameter("vehicle_id",vehicle_id);

    List<LocalDateTime> localDateTimes=query.getResultList();
    if(localDateTimes.size()==0)
        return new DefaultResponseDTO(200, ResponseStatus.OK,"Any Date",null);;
    return new DefaultResponseDTO(200, ResponseStatus.OK,"Possible Time",localDateTimes.get(0));

}

in the testing unit, I mocked the entity manager as below,

@Mock
EntityManager em;

And in the test method,

Query query = Mockito.mock(Query.class);
Mockito.when(em.createQuery(Mockito.anyString())).thenReturn(query);
Mockito.when(query.getResultList()).thenReturn(new LinkedList());

My question is, if I mock entity Manger as I mentioned above then the query in the method didn't get checked. Is it a bad coding practice?

Is there any other way to check queries without calling the database?

2
  • What purpose do you think your test serves? What's the outcome you are testing? Oct 1, 2021 at 15:28
  • @VincentSavard usually, unit testing is done to test the smallest piece of code that can be logically isolated in a system. But in this code example, the query that has been built inside the testing method shouldn't have to check? does query give the expected output or not? Oct 1, 2021 at 15:39

2 Answers 2

7

The objective of unit testing is to validate the outcome of a certain piece of code in isolation. By mocking the EntityManager, you are only validating that your implementation calls some methods, but it gives you no confidence on how this code will actually behave with the real dependencies. Even if you did make sure you sent the correct query and the correct parameters (by substituting the actual query instead of using any()), you would now be testing the implementation instead of the outcome, which negates the benefits of unit testing.

Not all code may be easily unit tested, and interaction with an external service such as a database system is generally one of those cases. You will find much more value by converting this test to an integration test, testing against an actual database.

This is also a consequence of your current architecture. The method getFirstReturnDate acts on multiple abstraction levels. It checks the database, validates some rules for presentation purposes and returns an HTTP response code. While there is nothing inherently wrong with this design choice (especially for a small CRUD service), it hinders unit testing. If you isolated each different abstraction to its own isolated context, you could easily validate the presentation rule (Are there any reservation time?) and the API layer (Does this return the correct HTTP status code?) in isolation, with unit tests.

1

Yes your test is not a real unit test. But maybe it can be used as a part of a system test for of you want to test your endpoints or a regression test.

It is Good practice to test on several levels. If you want to test the actual querries you do, i suggest using a H2 inmemory databasen for quick test results.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.