9

I want to create a callout exception in test class for covering batch apex callout. I am able to cover the try block but can't get, how to cover the catch block. Can i hardcode or put something like this CalloutException excpObj with some particular exception so that the control enters the catch block??

for (Contact cont : scope){
    try {
         XXXXXXXX
    } catch (CalloutException excpObj) {
         some_method_name(cont.Id,' Callout Exception','---- ERROR = ' + excpObj.getMessage(),cont);
    }
}
  • I don't think there is any way of constructing a CalloutException in Apex. If you are prepared to change your code to catch Exception, you can have an @TestVisible Boolean somewhere in the XXXXXXXX code that tells that code to throw a custom exception when the flag is set by the test. – Keith C Sep 18 '14 at 13:45
  • Yeah....@keith my conslusion was also somewhat same, i.e. setting a check flag.Apart from that nothing appears to work until now. Thanks keith. – novice Sep 18 '14 at 14:18
20

There is exactly one way to do this.

Scaffold a System.CalloutException in your mock:

@TestVisible class UnauthorizedEndpointResponse implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest request) {
        CalloutException e = (CalloutException)CalloutException.class.newInstance();
        e.setMessage('Unauthorized endpoint, please check Setup->Security->Remote site settings.');
        throw e;
    }
}

Then cause it to appear in your test with the normal HTTP mocks:

Test.setMock(HttpCalloutMock.class, new UnauthorizedEndpointResponse());
BatchClass.doTheCallout();
5

In addition to @regal's response, often you want to test what happens if you get a httpResponse status other than 200. Here is a code sample showing how to do that. You use arguments in the constructor of the Mock object to guide how the respond() method generates the response to the rest of your code.

The Mock class

public class myMock implements HttpCalloutMock {  
  Boolean  isMockResponseSuccessful;  // set by constructor, used to vary the mockresponse

  public myMock(Boolean isMockResponseSuccessful) {
    this.isMockResponseSuccessful  = isMockResponseSuccessful;
  }

  public HttpResponse respond(HttpRequest rqst) {
    HttpResponse hResp    = new HttpResponse();
    if (this.isMockResponseSuccessful) {
      hResp.setStatusCode(200);
      hResp.setBody(someJsonSuccessresponse);      
    }
    else {
      hResp.setStatusCode(400);
      hResp.setStatus('Bad request');
    }
    return hResp;
  }
}

The testmethods

private static void someTestmethodSuccess() {
  Test.startTest();
  Test.setMock(HttpCalloutMock.class, new myMock(true));  // mock with response = 200
  executeMymethod();
  System.assert(assertSomethingWhenExpectingSuccess);
  Test.stoptest();
}

private static void someTestmethodFailure() {
  Test.startTest();
  Test.setMock(HttpCalloutMock.class, new myMock(false));  // mock with response = 400
  executeMymethod();
  System.assert(assertSomethingWhenExpectingFailure);
  Test.stoptest();
}    
1

You can generate a callout exception this way:-

Try to perform a DML in between startTest() and stopTest();

It will generate:

System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

public static testmethod void testConstructor()    {

       Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
       Test.startTest();
           Account acc = new Account(Name='Test Ignore'); // any sobject for DML
           insert acc;
           /* Replace me with execute batch statement here in between DML */
           delete acc;
       Test.stopTest();
 }

Basically, you need to do a DML before calling out. And it should be in same transaction.

Or you can use Test.isRunningTest() in batch class to perform DML.

  • Hello @Ashwani, can you please explain this error and how to solve it, i am getting the same error. – Archita patnaik Jul 10 '17 at 10:12
  • The web service callout is triggered in an After Trigger (after insert) through @future method . so call out is called from a trigger, so data has to be inserted. In my test class, first i setup some test data(list of account record), then in between startTest() and stopTest(), i set up my mock callout responses, that is Test.setMock(), on second step i wrote "insert acc", here i am getting ,the above error. – Archita patnaik Jul 10 '17 at 10:36
  • @Architapatnaik can you make sure you are not inserting any data before the callout within future method – Ashwani Jul 10 '17 at 10:44
  • i dont think so. because, the scenario is, when any account is created(after insert), then it will call class method(@future), and that method is calling callout, post the data and give some response. so in test class, testing should be on "insert acc". – Archita patnaik Jul 10 '17 at 10:58
  • how can i use Test.isRunningTest() ? – Archita patnaik Jul 10 '17 at 11:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.