Spock - mocked repository method save() giving NullPointerException
Asked Answered
R

1

5

I'm using Spock to unit test a service in my app. Recently I encountered a weird behavior I don't really understand.

Here is my unit test:

def setup() {
    parkingSessionRepository = Mock(ParkingSessionRepository.class)
    parkingSessionMapper = Stub(ParkingSessionMapper.class)

    parkingMeterService = new ParkingMeterService(parkingSessionRepository, parkingSessionMapper)
}

def "should start parking session for vehicle that doesn't have an active parking session"() {

    given:
    String testVehicleId = "AWC1342"
    long testParkingRateId = 1

    ParkingStartDTO testParkingStartDTO = ParkingStartDTO.builder()
            .vehicleId(testVehicleId)
            .parkingRateId(testParkingRateId)
            .build()

    ParkingSession testParkingSession = ParkingSession.builder()
    .vehicleId(testVehicleId)
    .parkingRate(ParkingRate.REGULAR)
    .build()

    parkingSessionMapper.fromParkingStartDTO(_) >> testParkingSession
    parkingSessionRepository.save(_ as ParkingSession) >> testParkingSession

    parkingSessionRepository.findByVehicleIdAndStopTimeIsNull(_ as String) >> Optional.empty()

    when:
    ParkingMeterResponseDTO parkingMeterResponseDTO = parkingMeterService.startParkingMeter(testParkingStartDTO)

    then:
    1 * parkingSessionRepository.save(_ as ParkingSession)

    parkingMeterResponseDTO.vehicleId == testVehicleId
    Assert.assertNotNull(parkingMeterResponseDTO.parkingSessionId)
    Assert.assertNotNull(parkingMeterResponseDTO.timestamp)
}

While code for tested service method is:

public ParkingMeterResponseDTO startParkingMeter(final ParkingStartDTO parkingStartDTO) {

    String vehicleId = parkingStartDTO.getVehicleId();

    if (isParkingSessionAlreadyActive(vehicleId)) {
        throw new ParkingSessionAlreadyActiveException();
    } else {

        ParkingSession parkingSession = parkingSessionMapper.fromParkingStartDTO(parkingStartDTO);
        parkingSession.setStartTime(Timestamp.from(Instant.now()));
        parkingSession = parkingSessionRepository.save(parkingSession);

        return ParkingMeterResponseDTO.builder()
                .vehicleId(parkingSession.getVehicleId())
                .parkingSessionId(parkingSession.getId())
                .timestamp(parkingSession.getStartTime())
                .build();
    }
}

Now, when I run unit test there is a result of NullPointerException coming from 'when' block (save() method returns null). However, when I remove save() method check from 'then' block - everything works smoothly, no nulls.

What could be a reason for such behaviour? I suspect some mocking issues with Repository, but I'm not sure what exactly happens under the hood and how to resolve the issue so the save() method check in 'then' block works as intended?

Replacement answered 5/11, 2018 at 12:9 Comment(0)
P
7

Please check Spock documentation: http://spockframework.org/spock/docs/1.0/interaction_based_testing.html

Section: Combining Mocking and Stubbing

Combining:

given:
...
parkingSessionRepository.save(_ as ParkingSession) >> testParkingSession
...
then:
1 * parkingSessionRepository.save(_ as ParkingSession)

Will not work. You have to use one expression:

1 * parkingSessionRepository.save(_ as ParkingSession) >> testParkingSession
Perisarc answered 6/11, 2018 at 0:7 Comment(3)
Thank you, it works :) It didn't occur to me that this issue should be solved that way because apparently you need to either write mocking expression in 'then' block or assertion in 'when' block which I believe is a bit unelegant.Kamila
@PrzemysławEtz, please accept an answer if it solves your problem.Burnt
It did help me a lot. Thanks! :-)Alost

© 2022 - 2024 — McMap. All rights reserved.