2

I am using fflib_ApexMocks without any issue till today. However, for this case, I am not able to understand what I am doing wrongly. This is the magical mock:

mocks.when(appointmentServiceMock.isSuitableForAppointment(requestDTO)).thenReturn(true);

Basically using the object 'requestDTO' as parameter the mock is returning null instead of the Boolean. Interestingly, replacing this parameter for another one, for example an string, everything is fine and the boolean is returned...

Following the components:

Apex Resource

@RestResource(urlMapping='/appointment/form/validation')
global with sharing class AppointmentFormValidationRestResource {

    @HttpPost
    global static void validate() {

        AppointmentDTO requestDTO = (AppointmentDTO) JSON.deserialize(
                RestContext.request.requestBody.toString(),
                AppointmentDTO.class
        );

        FormValidationResponse formValidationResponseDTO = new FormValidationResponse();
System.debug('m:'+requestDTO);
        formValidationResponseDTO.isSuitableForAppointment = AppointmentService.isSuitableForAppointment(requestDTO);
        RestContext.response.addHeader('Content-Type', 'application/json');
        RestContext.response.statusCode = 200;
        RestContext.response.responseBody = Blob.valueOf(JSON.serialize(formValidationResponseDTO));

    }

}

Unit Test

    @IsTest static void validateTest() {

        // Given
        AppointmentDTO requestDTO = new AppointmentDTO();
        requestDTO.destinations = new List<String>{
                'Ibiza'
        };
        requestDTO.travelDetails.travelWhen = Date.today().addDays(100);
        requestDTO.travelDetails.travelPeople = 2;
        requestDTO.travelDetails.travelBudget = 1500;


        RestRequest request = new RestRequest();
        RestResponse response = new RestResponse();

        request.requestURI = URL.getSalesforceBaseUrl().toExternalForm() + '/services/apexrest/appointment/form/validation';
        request.httpMethod = 'POST';
        request.requestBody = Blob.valueOf(JSON.serialize(requestDTO));

        RestContext.request = request;
        RestContext.response = response;

        fflib_ApexMocks mocks = new fflib_ApexMocks();
        AppointmentServiceImpl appointmentServiceMock = (AppointmentServiceImpl) mocks.mock(AppointmentServiceImpl.class);

        mocks.startStubbing();
       System.debug('t:'+requestDTO);  
mocks.when(appointmentServiceMock.isSuitableForAppointment(requestDTO)).thenReturn(true);
        mocks.stopStubbing();

        Application.Service.setMock(IAppointmentService.class, appointmentServiceMock);

        // When
        Test.startTest();
        AppointmentFormValidationRestResource.validate();
        Test.stopTest();

        // Then
        System.assertEquals(200, response.statusCode, 'Error response status code');
        String responseBody = response.responseBody.toString();
        AppointmentFormValidationRestResource.FormValidationResponse r =
                (AppointmentFormValidationRestResource.FormValidationResponse) JSON.deserialize(
                        responseBody,
                        AppointmentFormValidationRestResource.FormValidationResponse.class);

        System.assertEquals(true,r.isSuitableForAppointment);
    }

Service

public with sharing class AppointmentServiceImpl implements IAppointmentService{
[....]

  public Boolean isSuitableForAppointment(AppointmentDTO appointmentDTO){
   [...]
  }

[....]
}

Finally the debug log, in case it can help to identify what is happening

20:39:28.2 (55457059)|USER_DEBUG|[30]|DEBUG|t:AppointmentDTO:[availableAgentIds=null, avoidCreateAppointment=false, calendarProviderEventId=null, consumer=Consumer:[consumerEmail=null, consumerLastName=null, consumerName=null, consumerPhone=null, consumerPostalCode=null], destinations=(Ibiza), endDate=null, eventId=null, office=null, onlineMeetingURL=null, recordId=null, selectedAgentId=null, startDate=null, travelDetails=TravelDetails:[travelBudget=1500, travelPeople=2, travelTellus=, travelWhen=2022-05-22 00:00:00]]
20:39:28.2 (55594654)|SYSTEM_MODE_ENTER|false
20:39:28.2 (69600119)|SYSTEM_MODE_EXIT|false
20:39:28.2 (69654529)|SYSTEM_MODE_ENTER|false
20:39:28.2 (69675333)|SYSTEM_MODE_EXIT|false
20:39:28.2 (69710481)|SYSTEM_MODE_ENTER|false
20:39:28.2 (69814101)|SYSTEM_MODE_EXIT|false
20:39:28.2 (69846058)|SYSTEM_MODE_ENTER|false
20:39:28.2 (69869949)|SYSTEM_MODE_EXIT|false
20:39:28.2 (118052970)|SYSTEM_MODE_ENTER|false
20:39:28.2 (118086146)|SYSTEM_MODE_EXIT|false
20:39:28.2 (118186176)|SYSTEM_MODE_ENTER|false
20:39:28.2 (122565005)|USER_DEBUG|[13]|DEBUG|m:AppointmentDTO:[availableAgentIds=null, avoidCreateAppointment=false, calendarProviderEventId=null, consumer=Consumer:[consumerEmail=null, consumerLastName=null, consumerName=null, consumerPhone=null, consumerPostalCode=null], destinations=(Ibiza), endDate=null, eventId=null, office=null, onlineMeetingURL=null, recordId=null, selectedAgentId=null, startDate=null, travelDetails=TravelDetails:[travelBudget=1500, travelPeople=2, travelTellus=, travelWhen=2022-05-22 00:00:00]]
20:39:28.2 (126263753)|SYSTEM_MODE_EXIT|false
20:39:28.2 (129415400)|EXCEPTION_THROWN|[49]|System.AssertException: Assertion Failed: Expected: true, Actual: null
20:39:28.2 (129750666)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: true, Actual: null

Class.AppointmentFormValidationRestTest.validateTest: line 49, column 1
20:39:28.2 (129766555)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: true, Actual: null

Class.AppointmentFormValidationRestTest.validateTest: line 49, column 1

Any idea??

1 Answer 1

1

Here's one possibility based on this list of possible apex mocks issues

You are using Apex types as arguments to stubbed methods without a way to test equality

mocks.when(appointmentServiceMock.isSuitableForAppointment(requestDTO))
     .thenReturn(true);

Since requestDTO is an object of type AppointmentDTO, the default equals matcher being used in appointmentServiceMock.isSuitableForAppointment(requestDTO) will not find a match against the actual arg passed at runtime unless you have implemented an equals(Object compareTo) and hashcode() public method for type AppointmentDTO. That is, custom apextypes can't be compared for equality unless you add these two methods to the class

You can work around this at some cost in unit test completeness by using the anyObject matcher

mocks.when(appointmentServiceMock.isSuitableForAppointment((fflib_Match.anyObject)requestDTO))

which won't care what actual requestDTO is passed to the underlying isSuitableForAppointment method to mock the return value

Notes:

1 - Be aware when implementing equals(Object compareTo) that JSON.serialize is non-deterministic in how it renders its results so comparing two serialized JSONs to each other might not always work, even if the component elements are identical

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .