function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Nigel DendyNigel Dendy 

How to Create a Test class with a Future Callout that is not dependent on an HTTP Response?

I am new to Apex, but I have been tasked with sending a Salesforce Custom Object to another Company system. My Class works as expected, but my test class fails due to having a @future method. I did look into the Mock callout classes but those seem to be aimed at getting a fake response but I don't really have any logic based around the response.

My Test Class code coverage is only at 4% because my createRecord method calls the sendCase method which has the Http Callout in it.

So my question is: Do I write the Mock Class for the 'sendCase` method? Or will I need to completely refactor my code?

Any help would be greatly appreciated .

Reference Links
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_testing_httpcalloutmock.htm
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_wsdl2apex_testing.htm
https://hicglobalsolutions.com/blog/how-to-create-a-mock-callout-to-test-the-apex-rest-callout-in-salesforce/

My Class
public without sharing class Processmaker_CreateSiteSurvey {
        
    public string endpoint;
    public string pro_uid;
    public string tas_uid;

    public Processmaker_CreateSiteSurvey() {
        //endpoint = 'https://<redacted>/api/1.0/workflow/cases'; // Prod Processmaker Endpoint
        endpoint = 'https://<redacted>/api/1.0/workflow/cases'; // Dev Processmaker Endpoint
        pro_uid = '308138621584f0518bd8f89094895074';
        tas_uid = '562545883584f060a62b559007702299';
    }

    /*-----------------------------------------
    createRecord() - - Invocable Method
    This methods builds a ProcessMaker case using the invocable variable
    -----------------------------------------*/
    @InvocableMethod (label='Create a New Site Survey')
    public static void createRecord(List<Create_Case_Params> params) {
        
        Processmaker_CreateSiteSurvey p0 = new Processmaker_CreateSiteSurvey();
        PM_SiteSurvey cs1 = new PM_SiteSurvey();
        PM_SiteSurvey.Variables cv1 = new PM_SiteSurvey.Variables();
        List<PM_SiteSurvey.Variables> lcv1 = new List<PM_SiteSurvey.Variables>();
        Processmaker_Case__c fc1 = new Processmaker_Case__c();

        String recordTypeID = [Select Id FROM RecordType WHERE Name = 'Business Site Survey'].Id;

        //Salesforce Field Check Record
        for (Create_Case_Params param: params) {
            String servSelected = string.join(param.surveySelection, ';');
            fc1.Survey_Selection_s__c = servSelected;  
            fc1.SF_User__c = param.lookupUser;
            fc1.SF_Contact__c = param.lookupContact;
            fc1.SF_Account__c = param.lookupAccount;
            fc1.Service_Rep__c = param.origName;
            fc1.Contact_Number__c = param.contactNum;
            fc1.Service_Area__c = param.serviceArea;
            fc1.Submitted_Date__c = param.submitDate;
            fc1.Site_Survey_Scheduled__c = param.sssDate;
            fc1.Site_Survey_Scheduled_Time__c = param.sssTime;
            fc1.Business_Name__c = param.busName;
            fc1.Business_Address__c = param.busAddy;
            fc1.Local_Contact__c = param.localContact;
            fc1.Local_Contact_Phone__c = param.localContactPhone;
            fc1.Other__c = param.surveySelectionOther;
            fc1.Remarks__c = param.remarks;
            fc1.RecordTypeId = recordTypeID;
        }

        try {
            insert fc1;
        } catch (StringException e) {
            System.debug('An error has occurred: ' + e.getMessage());
        }

        //Processmaker Field Check Array
        for (Create_Case_Params param: params) {
            cs1.pro_uid = p0.pro_uid;
            cs1.tas_uid = p0.tas_uid;
            cv1.CustomerSvcFullName = param.origName;
            cv1.CustomerSvcPhone = param.contactNum;
            cv1.ServiceArea = param.serviceArea;
            cv1.ServiceArea_label = param.serviceArea_label; //label
            cv1.SubmitDate = param.submitDate;
            cv1.SubmitDate_label = param.subDate_label; //label
            cv1.ScheduledSiteSurveyDate = param.sssDate;
            cv1.ScheduledSiteSurveyDate_label = param.siteSurveySched_label; //label
            cv1.ScheduledSiteSurveyTime = param.sssDate + ' ' + param.sssTime;
            cv1.ScheduledSiteSurveyTime_label = param.sssTime;
            cv1.BusinessName = param.busName;
            cv1.BusinessAddress = param.busAddy;
            cv1.BusinessLocalContact = param.localContact;
            cv1.BusinessContactTelephone = param.localContactPhone;
            cv1.SurveySelection = param.surveySelection;
            String surveySelection_label = json.serialize(param.surveySelection_label);
            cv1.SurveySelection_label = surveySelection_label; //label
            cv1.SelectionOther = param.surveySelectionOther;
            cv1.SelectionOther_label = param.surveySelectionOther;
            cv1.RequestRemarks = param.remarks;
            cv1.SalesforceRecordID = fc1.Id;
            cv1.CustomerSvcUserID = [SELECT SamAccountName__c FROM User WHERE Id = :param.lookupUser].SamAccountName__c;
            lcv1.add(cv1);
            cs1.variables = lcv1;
        }
        sendCase(json.serialize(cs1), fc1.Id);
    }

    /*-----------------------------------------
    sendCase() - returns ProcessMaker Response
    This method send the case to Processmaker via HTTP POST Request
    -----------------------------------------*/
    @future(callout = true)
    public static void sendCase(String payload, String recordID) {
        
        Processmaker_CreateSiteSurvey p1 = new Processmaker_CreateSiteSurvey();
        String access_token = Processmaker_Connector.getloginToken();

        try {
            HttpRequest req = new HttpRequest();
            req.setMethod('POST');
            req.setEndpoint(p1.endpoint);
            req.setHeader('Content-Type', 'application/json; charset=utf-8');
            req.setHeader('Authorization', 'Bearer ' + access_token);
            req.setBody(payload);

            Http binding = new Http();
            HttpResponse res = binding.send(req);
            ProcessmakerResponse pr1 = (ProcessmakerResponse) JSON.deserialize(res.getBody(), ProcessmakerResponse.class);
            Processmaker_ExecuteTrigger.executeTrigger(pr1.app_uid, '478587874638e4d6b391802022646525');
            Processmaker_RouteCase.routeCase(pr1.app_uid);
            Processmaker_Case__c caseRecord = new Processmaker_Case__c(
                Id = recordID,
                app_uid__c = pr1.app_uid,
                Case_Number__c = pr1.app_number
            );
            upsert caseRecord; 
            System.debug('Case Creation Successful');
        } catch (StringException e) {
            System.debug('An error has occurred: ' + e.getMessage());
        }
    }

    /*----------------------------------------------
    Create Case Params - Class - Invocable Variables
    This class houses all of the invocalble variables for the createCase() invocable method
    ----------------------------------------------*/
    public class Create_Case_Params {

        @InvocableVariable(label='Originator Name' required=true)
        public String origName; 

        @InvocableVariable(label='Contact Number' required=true)
        public String contactNum; 

        @InvocableVariable(label='Service Area' required=true)
        public String serviceArea; 

        @InvocableVariable(label='Submission Date' required=true)
        public String submitDate; 

        @InvocableVariable(label='Site Survey Scheduled' required=true)
        public String sssDate; 

        @InvocableVariable(label='Siter Survey Scheduled (Time)' required=true)
        public String sssTime; 

        @InvocableVariable(label='Business Name' required=true)
        public String busName; 

        @InvocableVariable(label='Business Address' required=true)
        public String busAddy; 

        @InvocableVariable(label='Local Contact for Business' description='must be AptSelectionApartment, AptSelectionSuite, AptSelectionLot, AptSelectionNA' required=true)
        public String localContact; 

        @InvocableVariable(label='Local Contact Phone' required=true)
        public String localContactPhone; 

        @InvocableVariable(label='Survey Selection' description='values must be SurveySelectionVoIP, SurveySelectionWiring, SurveySelectionKeySystem, SurveySelectionMETS, SurveySelectionZipstream' required=true)
        public List<String> surveySelection;
        
        @InvocableVariable(label='Survey Selection Other')
        public String surveySelectionOther;

        @InvocableVariable(label='Remarks' required=true)
        public String remarks;

        //Labels
        @InvocableVariable(label='Label: Survey Selection')
        public List<String> surveySelection_label;

        @InvocableVariable(label='Label: Site Survey Date Scheduled')
        public String siteSurveySched_label;

        @InvocableVariable(label='Label: Site Survey Time Scheduled')
        public String siteSurveySchedTime_label;

        @InvocableVariable(label='Label: Service Area')
        public String serviceArea_label;

        @InvocableVariable(label='Label: Submission Date')
        public String subDate_label;

        //Salesforce Fields
        @InvocableVariable(label='Account' )
        public String lookupAccount;

        @InvocableVariable(label='Contact' )
        public String lookupContact;

        @InvocableVariable(label='User' )
        public String lookupUser;
    }
}


My Test Class
@isTest
public without sharing class Processmaker_CreateSiteSurveyTest {
    
    public Processmaker_CreateSiteSurveyTest() {

    }

    //--------Data Generation

    @isTest
    public static List<Processmaker_CreateSiteSurvey.Create_Case_Params> createTestData() {

        //Create a test Account
        Account testAccount = new Account(
            Name = 'Salesforce Testing',
            Phone = '0123456789',
            BillingStreet = '123 Fake Street',
            BillingCity = 'Fake City',
            BillingState = 'Fake State',
            BillingPostalCode = '12345',
            BillingCountry = 'Fake Country'
        );
        insert testAccount;

        //Create a test Contact
        Contact testContact = new Contact(
            FirstName = 'Salesforce',
            LastName = 'Testing',
            Phone = '0123456789',
            AccountId = testAccount.Id
        );
        insert testContact;
        
        //Get the User, Account and Contact IDs
        String testSFUser = [SELECT Id FROM User WHERE Name = 'Nigel Dendy'].Id;

        //Create Case Params
        List<Processmaker_CreateSiteSurvey.Create_Case_Params> caseParams = new List<Processmaker_CreateSiteSurvey.Create_Case_Params>();
        Processmaker_CreateSiteSurvey.Create_Case_Params caseParam = new Processmaker_CreateSiteSurvey.Create_Case_Params();
        caseParam.origName = 'Salesforce Testing';
        caseParam.contactNum = '0123456789';
        caseParam.serviceArea = 'ServiceAreaRKHL';
        caseParam.submitDate = '2023-02-16';
        caseParam.sssDate = '2023-02-21';
        caseParam.sssTime = '08:24';
        caseParam.busName = 'TestBusinessName';
        caseParam.busAddy = '123 Fake Address';
        caseParam.localContact = 'Bob Barker';
        caseParam.localContactPhone = '0123456789';
        caseParam.surveySelection = new List<String>{'SurveySelectionVoIP','SurveySelectionWiring'};
        caseParam.surveySelectionOther = 'CustomRequest';
        caseParam.remarks = 'blah blah blah blah blah';

        //Labels
        caseParam.surveySelection_label = new List<String>{'VoIP','Wiring'};
        caseParam.siteSurveySched_label = '2023-02-21';
        caseParam.siteSurveySchedTime_label = '08:24';
        caseParam.serviceArea_label = 'ServiceAreaRKHL';
        caseParam.subDate_label = '2023-02-16';

        //Salesforce Fields
        caseParam.lookupAccount = testAccount.Id;
        caseParam.lookupContact = testContact.Id;
        caseParam.lookupUser = testSFUser;

        caseParams.add(caseParam);

        return caseParams;
    }

    //--------Tests

    @isTest
    public static void Processmaker_CreateSiteSurveyTest() {
        Processmaker_CreateSiteSurvey testClass = new Processmaker_CreateSiteSurvey();
    }

    @isTest
    public static void createCaseTest() {
        List<Processmaker_CreateSiteSurvey.Create_Case_Params> testClass = createTestData();
        Processmaker_CreateSiteSurvey.createRecord(testClass);
    }
}

 
Best Answer chosen by Nigel Dendy
Prateek Prasoon 25Prateek Prasoon 25
Answer :-

To create a test class with a future callout that is not dependent on an HTTP response, you can use the Test.setMock method to set a mock response for the callout. This allows you to simulate the callout without actually making the HTTP request.
 
@isTest
private class MyTestClass {
    private class MockHttpResponse implements HttpCalloutMock {
        public HttpResponse respond(HttpRequest req) {
            // Create a mock response object
            HttpResponse res = new HttpResponse();
            res.setStatusCode(200);
            res.setBody('{"success": true}');
            return res;
        }
    }

    @future(callout=true)
    static void makeCallout() {
        // Make the callout
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://www.example.com');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    }

    static testMethod void testMakeCallout() {
        // Set the mock response
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse());

        // Call the future method
        Test.startTest();
        makeCallout();
        Test.stopTest();
    }
}

If you find my answer helpful, please mark it as the best answer.Thanks!

All Answers

Prateek Prasoon 25Prateek Prasoon 25
Answer :-

To create a test class with a future callout that is not dependent on an HTTP response, you can use the Test.setMock method to set a mock response for the callout. This allows you to simulate the callout without actually making the HTTP request.
 
@isTest
private class MyTestClass {
    private class MockHttpResponse implements HttpCalloutMock {
        public HttpResponse respond(HttpRequest req) {
            // Create a mock response object
            HttpResponse res = new HttpResponse();
            res.setStatusCode(200);
            res.setBody('{"success": true}');
            return res;
        }
    }

    @future(callout=true)
    static void makeCallout() {
        // Make the callout
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://www.example.com');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    }

    static testMethod void testMakeCallout() {
        // Set the mock response
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse());

        // Call the future method
        Test.startTest();
        makeCallout();
        Test.stopTest();
    }
}

If you find my answer helpful, please mark it as the best answer.Thanks!
This was selected as the best answer
Nigel DendyNigel Dendy
This was exactly what I needed! Thank you so much!