We have a callout to an external webservice that requires retrieving some data from the database via SOQL and then issuing an HTTP GET request, and performing various logic based upon the response. Trying to make our unit tests as robust as possible, we try to always create test data from within the unit tests. However, it seems that creating test data within a unit test, and then trying to do a callout, even when using HttpCalloutMock and Test.startTest() and Test.stopTest() will still throw an exception.
While we could use some hard-coded values in the callout unit-test, we'd really prefer not to, as we'd like to have our tests cover as much of the end-to-end as possible, including retrieving the data from the database (which won't exist unless we create it from within the unit test).
We could separate the logic out into multiple unit tests, one for testing the retrieving of the data, and another for testing the callout mechanism, but I'd worry there may be logic branches that rely on not only data in the database, but also certain HttpResponses that may be hard to traverse without muddying-up the actual non-test code.
Here is a very much simplified example that shows the problem.
Callout Class:
public class CalloutClass {
public static HttpResponse getInfoFromExternalService(Id myCaseId) {
Case myCase = [SELECT Id, Account.Integration_Key__c FROM Case WHERE Id = :myCaseId LIMIT 1];
HttpRequest req = new HttpRequest();
req.setEndpoint('http://api.salesforce.com/foo/bar?id=' + myCase.Account.Integration_Key__c);
req.setMethod('GET');
Http h = new Http();
HttpResponse res = h.send(req);
return res;
}
}
And the HttpCalloutMock Class:
@isTest
global class ExampleCalloutMock implements HttpCalloutMock{
global HttpResponse respond(HTTPRequest req){
HttpResponse res = new HttpResponse();
res.setStatus('OK');
res.setStatusCode(200);
res.setBody('GREAT SCOTT');
return res;
}
}
And the TestClass:
@isTest(seeAllData=False)
private class ExampleCallout_Test{
static Case getTestCase(){
Case myCase = new Case(Subject = 'Test');
insert myCase;
return myCase;
}
static testMethod void shouldBeAbleToGetData(){
Case myCase = getTestCase();
Test.startTest();
Test.setMock(HttpCalloutMock.class, new ExampleCalloutMock());
HttpResponse res = CalloutClass.getInfoFromExternalService(myCase.Id);
Test.stopTest();
}
}
And here is the result:
15:30:18.239 (5239722000)|EXCEPTION_THROWN|[45]|System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
I'm curious if perhaps I'm missing something, if this is a bug, or is it by design to not allow setting up test data when performing a mocked callout? If there are any creative solutions or workarounds I'd love to hear them as well. :)