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
Stuart GrimshawStuart Grimshaw 

How do I unit test a @future method that makes a callout?

I have a trigger that fires when an opportunity is updated, as part of that I need to call our API with some detail from the opportunity.

As per many suggestions on the web I've created a class that contains a @future method to make the callout.

I'm trying to catch an exception that gets thrown in the @future method, but the test method isn't seeing it.

The class under test looks like this:

public with sharing class WDAPIInterface {
    public WDAPIInterface() {

    }

    @future(callout=true) public static void send(String endpoint, String method, String body)  {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(endpoint);
        req.setMethod(method);
        req.setBody(body);

        Http http = new Http();
        HttpResponse response = http.send(req);

        if(response.getStatusCode() != 201) {
            System.debug('Unexpected response from web service, expecte response status status 201 but got ' + response.getStatusCode());
            throw new WDAPIException('Unexpected response from web service, expecte response status status 201 but got ' + response.getStatusCode());
        }
    }
}

here's the unit test:

@isTest static void test_exception_is_thrown_on_unexpected_response() {
        try {
            WDHttpCalloutMock mockResponse = new WDHttpCalloutMock(500, 'Complete', '', null);
            WDAPIInterface.send('https://example.com', 'POST', '{}');
        } catch (WDAPIException ex) {
            return;
        }

        System.assert(false, 'Expected WDAPIException to be thrown, but it wasnt');
    }
Now, I've read that the way to test @future methods is to surround the call with Test.startTest() & Test.endTest(), however when I do that I get another error:

METHOD RESULT 
test_exception_is_thrown_on_unexpected_response : Skip

 MESSAGE 
Methods defined as TestMethod do not support Web service callouts, test skipped
So the question is, how do I unit test a @future method that makes an callout?

alibzafaralibzafar
Use Test.startTest() then run your Test and finish by Test.stopTest().  After that you can do your assertions. 

Try something like 
@isTest static void test_exception_is_thrown_on_unexpected_response() {
      Test.startTest()
  try {
            WDHttpCalloutMock mockResponse = new WDHttpCalloutMock(500, 'Complete', '', null);
            WDAPIInterface.send('https://example.com', 'POST', '{}');
        } catch (WDAPIException ex) {
            return;
        }
Test.stopTest()

        System.assert(false, 'Expected WDAPIException to be thrown, but it wasnt');
    }

Stuart GrimshawStuart Grimshaw
I did say in my original question that I'd tried that, but I added it immediatly either side of the @future method:
Test.startTest()
WDAPIInterface.send('https://example.com', 'POST', '{}');
Test.stopTest();
So I tried it the way you are suggesting and I still get the same error:
Operation: Running Apex unit tests for: WDAPIInterfaceTest
Timestamp: Fri, 05 Sep 2014 11:58:29
   Result: [TEST RESULT]: FAIL

 METHOD RESULT 
test_exception_is_thrown_on_unexpected_response : Skip

 MESSAGE 
Methods defined as TestMethod do not support Web service callouts, test skipped


Gaurav NirwalGaurav Nirwal
Test.startTest()
 try {
       WDHttpCalloutMock mockResponse = new WDHttpCalloutMock(500, 'Complete', '', null);
            WDAPIInterface.send('https://example.com', 'POST', '{}');
        } catch (WDAPIException ex) {
            return;
        }
Test.stopTest()
System.assert(false, 'Expected WDAPIException to be thrown, but it wasnt');
    }
You can try this 
Stuart GrimshawStuart Grimshaw
@Matthews, I already said I tried that & it didn't work.
Kendrick ClineKendrick Cline

This was posted a long time ago. But the solution is to place your @future method with the Test.startTest and Test.stopTest code blocks and place your try catch around Test.startTest and Test.stopTest.

 

The exception is bubbled up.

try {
	Test.startTest();
	// PERFORM @future METHOD HERE
	Test.stopTest();
	} catch (Exception ex) {
	        success = true;
	}
}