Search code examples
springhibernatemockingmockitointegration-testing

hibernate - why mocked method throws NullPointerException


I'm trying to prepare some easy parking service api and i'm stuck at testing.

i have ParkingService like:

@Service
@RequiredArgsConstructor
public class ParkingService {
      private final CarsAndParkingsRepository carsAndParkingsRepository;
      private final ParkingsRepository parkingsRepository;

and simple custom method in repositories

public boolean checkFreeSlots(String idParking) {
    return carsAndParkingsRepository.findAmountOfTakenSlotsOnParking(idParking)
            < parkingsRepository.findByIdParking(idParking).getNumberOfParkingSlots();
}

and test:

 @Test
public void checkFreeSlotsIfIsCorrect() {
    //given

    //when
    when(carsAndParkingsRepository.findAmountOfTakenSlotsOnParking("5")).thenReturn(5);
    when(parkingsRepository.findByIdParking("5").getNumberOfParkingSlots()).thenReturn(10);
    when(parkingService.checkFreeSlots("5")).thenCallRealMethod();
    boolean result = parkingService.checkFreeSlots("5");

    //then
    assertEquals(true, result);
}

but the problem is i'm getting NullPointerException at line:

when(parkingsRepository.findByIdParking("5").getNumberOfParkingSlots()).thenReturn(10);

Previous mocked method when(carsAndParkingsRepository.findAmountOfTakenSlotsOnParking("5")).thenReturn(5) works correctly, its return 5 but in next line other repository throws exception. Is there any other way to test it or i'm doing something wrong? Ofc, i have mocked repositories :)


Solution

  • The problem is in line:

    when(parkingsRepository.findByIdParking("5").getNumberOfParkingSlots()).thenReturn(10);
    

    The argument to when needs to get evaluated before the call:

    • parkingsRepository.findByIdParking("5") is called and return null
    • null.getNumberOfParkingSlots() is called and throws NPE

    You have following options:

    Use a POJO for the Parking

    var parking5 = new Parking(
        5, //id
       10, //number of slots
       // ... other arguments
    );
    when(parkingsRepository.findByIdParking("5")).thenReturn(parking5);
    

    This is the preferred option, but if your object is difficult to construct (it should not be the case for hibernate entity), you can use a mock

    Use an mock of the Parking

    var parking5 = mock(Parking.class);
    when(parkingsRepository.findByIdParking("5")).thenReturn(parking5);
    when(parking5.getNumberOfParkingSlots()).thenReturn(10);
    

    Note that you can use chained calls in this approach, but this requires that you stub the methods in order.

    var parking5 = mock(Parking.class);
    when(parkingsRepository.findByIdParking("5")).thenReturn(parking5);
    when(parkingsRepository.findByIdParking("5").getNumberOfParkingSlots()).thenReturn(10);
    

    In my opinion, first flavour is easier to use, and lets you to reorder stubbings.