0

In a trigger, I have an asynchronous callout method in a class (In this class I have a method to build the json and another method to doing the callout) where I pass the json body string as below:

@future(callout=true) //asynchronous callout
public static void sendPayload(String jsonPayload) {
    
    String payload = jsonPayload;
    // send payload
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://th-apex-http-callout.herokuapp.com/');
    request.setMethod('POST');
    request.setHeader('Content-Type', 'application/json;charset=UTF-8');
    // Set the body as a JSON object
    request.setBody(payload);
    HttpResponse response = http.send(request);
    // Parse the JSON response
    if (response.getStatusCode() != 201) {
        System.debug('The status code returned was not expected: ' +
        response.getStatusCode() + ' ' + response.getStatus());
    } else {
        System.debug(response.getBody());
        transItemsSent = true;
    } 
}

I also have the following class:

global with sharing class TransactionHttpCalloutMock implements HttpCalloutMock {
    // Implement this interface method
    global HttpResponse respond(HttpRequest request) {

        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{ "keys": { "TransactionID": "00Q0p000001PvhZ" }, '+
            '"values": { "TransactionDate": "2020-01-20" } }');
        response.setStatusCode(200);
        return response;
    }
}

Then in my test class I have the following:

@isTest
private with sharing class TransactionBuildSendJson_Test {
    @isTest
    static void testTransactionCallout() {      // Test sending transaction json
        // Set mock callout class
        Test.setMock(HttpCalloutMock.class, new TransactionHttpCalloutMock());
        // This causes a fake callout to be sent from the class
        // that implements HttpCalloutMock.

        // Call the method that sends the paylaod
        HttpResponse response = TransactionBuildSendJson.sendPayload();
        
        // Verify that the responses receive contains the fake values sent
        String contentType = response.getHeader('Content-Type');
        System.assert(contentType == 'application/json', 
            'Expected content type is application/json');
        
        String actualValue = response.getBody();
        System.debug('Actual response body = ' + actualValue);

        String expectedValue = '{ "keys": { "TransactionID": "00Q0p000001PvhZ" }, '+
            '"values": { "TransactionDate": "2020-01-20" } }';           

        System.assertEquals(expectedValue, actualValue, 
            'Actual and expected json values doesn\'t match');

        System.assertEquals(200, response.getStatusCode(), 
            'Json not sent successfully');  
    }
}

While calling the TransactionBuildSendJson.sendPayload() method and since I am not passing a value, i get the incorrect signature error. If I pass the value I get illegal assignment from void error since the @future sendPayload has to be a void method.

Any ideas on how I can get around this and get the test class to work to get the coverage? I have to http post request to make so I use the same send Payload method by passing in the payload to call it twice. Thanks.

2 Answers 2

3

Calling:

Test.stopTest();

straight after your TransactionBuildSendJson.sendPayload will ensure that the asynchronous code is run as that method does this

All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously.

The TransactionBuildSendJson.sendPayload method in real use might need to e.g. do a database update to record its results so your test can assert that data.

Or if you just want to confirm that the call is made, keep a reference to the TransactionHttpCalloutMock instance and assert a property of that class set when its respond method is called.

You could also add an extra method and call that from your test:

@Future(callout=true)
public static void sendPayload(String jsonPayload) {
    synchronousSendPayload(jsonPayload)
}

@TestVisible
private static String synchronousSendPayload(String jsonPayload) {
    ...
    return ...;
}

but that is obscuring the lack of logic to record the results in the existing method.

Best to write tests with the goal of confirming that the logic is correct; coverage is just an indicator of a lack of test code and is not a sufficient measure in itself.

1

Thanks for the pointer @Keith-c. It helped on my second try :)

I updated it as below:

Test.startTest();
TransactionBuildSendJson.sendPayload('testpayload');
Test.stopTest();

I also removed the rest of the assert lines. Will still need to figure out on how to do the asserts now.

and the post here helped too: how to test a future method which makes callouts?.

Hope these helps someone further.

You must log in to answer this question.