[SalesForce] how to test a future method which makes callouts

I am practising the Integration workbook provided by salesforce. In tutorial#2 there's a class which makes future callouts to a Heroku app.This class is called by an after update trigger.
Callout class:

public class UpdateHeroku {
 public class ExternalOrder{
        public string id{get;set;}
        public Integer order_number{get;set;}
    } 

    @future(callout=true)
    public static void postOrder(list<id> invoiceids){
        JSONGenerator gen = Json.createGenerator(true);
        gen.writeStartArray();
        for(id invoiceid:invoiceids){
            gen.writeStartObject();
            gen.writeStringField('id', invoiceid);
            gen.writeEndObject();
        }
        gen.writeEndArray();

        string jsonOrders = gen.getAsString();
        System.debug('jsonOrders: '+ jsonOrders);

        HTTPrequest req = new HTTPrequest();
        req.setMethod('POST');
        req.setEndpoint('https://dry-springs-6303.herokuapp.com');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(jsonOrders);
        req.setTimeout(30000);

        Http http = new Http(); 
        HttpResponse res = http.send(req);
        System.debug('Fulfillment service returned '+ res.getBody());

        if(res.getStatusCode()!=200){
            System.debug('Error from '+ req.getEndpoint() + ':'+ res.getStatusCode() + ' ' + res.getStatus());
        }
        else
        {
            list<invoice__c> invoices = [select id from Invoice__c where id in :invoiceids];
            list<externalorder> orders = (list<ExternalOrder>)JSON.deserialize(res.getBody(), list<ExternalOrder>.class);

        Map<Id, Invoice__c> invoiceMap = new Map<Id, Invoice__c>(invoices);
        for(externalorder order:orders){
            Invoice__c invoice = invoiceMap.get(order.id);
            invoice.orderid__c = String.valueOf(order.order_number);
            }
            update invoices;
        } 
    }
}

I have written a test class for trigger which covers 62% of the callout class. I am figuring a way to separately unit test the callout class & wrote an implementation of mock interface

Test Class- which lead to code coverage of 66%

 @isTest
    public class UpdateHeroku_Test {
        @isTest static void testCallout(){
            Test.setMock(HttpcalloutMock.class, new MockGenerator_Test());

            list<invoice__c> invoices = new list<invoice__c>();
            list<id> invoiceids = new list<id>();
            invoices.add(new invoice__c(status__c='Negotiating'));
            invoices.add(new invoice__c(status__c='Pending'));
            insert invoices;
            invoices[0].status__c = 'Closed';
            invoices[1].status__c = 'Closed';
            update invoices;
            invoiceids.add(invoices[0].id);
            invoiceids.add(invoices[1].id);
            Test.startTest();
            UpdateHeroku.postOrder(invoiceids);
            Test.stopTest();
            System.assertEquals(expected1, invoice[0].orderid__c);
            System.assertEquals(expected2, invoice[1].orderid__c);
        }

global class MockGenerator_Test implements HttpCalloutMock{
        global httpResponse respond(HTTPrequest req){

            System.assertEquals('https://dry-springs-6303.herokuapp.com', req.getEndpoint());
            System.assertEquals('POST', req.getMethod());

            HttpResponse res = new HttpResponse();
            res.setHeader('Content-Type', 'application/json');
            res.setStatusCode(405);
            res.setStatus('Method Not Allowed');
            return res;
        }
      }

}

Since future methods can only return void type. I can't verify values received in HttpResponse using assertEquals method. Something like below which I intend to –

String contentType = res.getHeader('Content-Type');
System.assert(contentType == 'application/json');
String actualValue = res.getBody();
String expectedValue = '{"foo":"bar"}';
System.assertEquals(actualValue, expectedValue);

How can I verify the response returned? Thanks,

Best Answer

Here is where Test.startTest() and Test.stopTest() come in to play.

Structure your test to do all the data setup, then call startTest(). Execute your tested method and then call StopTest().

Among the many things this does, is force @future methods and callouts to fire. If you couple start/stopTest with mocked http callouts and you should be good to go.

Related Topic