13

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);
    }
}
2
  • 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
    Commented Sep 18, 2014 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
    Commented Sep 18, 2014 at 14:18

3 Answers 3

29

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();
3
  • 1
    I am trying this in my test class. However, the test class is failing. How do I make it pass? Commented Dec 28, 2021 at 2:30
  • same as above, i get coverage on the exception, but the test shows as failing.
    – gorav
    Commented Oct 30, 2023 at 19:49
  • 1
    update: wrapping the callout in the text in a try/catch to catch calloutException worked.
    – gorav
    Commented Oct 30, 2023 at 19:55
11

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.

8
  • Hello @Ashwani, can you please explain this error and how to solve it, i am getting the same error. Commented Jul 10, 2017 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. Commented Jul 10, 2017 at 10:36
  • @Architapatnaik can you make sure you are not inserting any data before the callout within future method
    – Ashwani
    Commented Jul 10, 2017 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". Commented Jul 10, 2017 at 10:58
  • how can i use Test.isRunningTest() ? Commented Jul 10, 2017 at 11:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .