We have method EnrollmentUtil.createNewEnrollment which contains a http callout on create of custom object Enrolment__c, and a test class to test this method. I'm trying to figure out how this class was passing 2 months ago when it was deployed to production, but with no changes since then it is now failing in production and sandboxes. On running EnrollmentUtil_Test we're getting the "You have uncommitted work pending. Please commit or rollback before calling out" error on EnrollmentUtil line 57 when the http callout is being made.
Apex Class code snippet
public class EnrollmentUtil {
public static string response {get;set;}
public static boolean disableTrigger {get{if(disableTrigger == null) disableTrigger = false; return disableTrigger;} set;}
public static void newEnrollment(map<Id,Enrolment__c> mapEnrollments) {
if(Test.isRunningTest() && Limits.getFutureCalls() >= Limits.getLimitFutureCalls()) {
system.debug(LoggingLevel.Error, 'Future method limit reached. Skipping...');
} else {
if(!Test.isRunningTest() && System.IsBatch() == false && System.isFuture() == false) {
createNewEnrollment(mapEnrollments.keySet());
}
}
}
@future(callout=true)
public static void createNewEnrollment(set<Id> enrollmentIds) {
System.debug(loggingLevel.Error, 'enrollmentIds==>' + enrollmentIds);
list<Enrolment__c> listEnrollments = new list<Enrolment__c>([select Id, Opportunity__r.Account.Name, Opportunity__r.Account.Id, Opportunity__r.Account.PersonEmail, Opportunity__r.Related_Course__r.LMS_Code__c from Enrolment__c where Id in: enrollmentIds]);
list<Account> listAccountUpdate = new list<Account>();
for(Enrolment__c e: listEnrollments) {
/* Step 1: Add the user if they don't already exist */
//Callout to search for the user via email
String userSearch = 'url string';
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(userSearch);
req.setMethod('GET');
HttpResponse res = h.send(req); // <---- Where "uncommitted work pending" error is thrown
response = res.getBody();
System.debug(loggingLevel.Error, '*** response: ' + response);
// code to parse json and form list of accounts to update if user found
}
}
//Update the account
update listAccountUpdate;
}
Test class:
@isTest
private class EnrollmentUtil_Test {
static testMethod void testNewEnrollment(){
RecordType rtSA = [Select Id from RecordType Where Name = 'Student Account'];
Account acc = new Account();
acc.FirstName = 'accFirst';
acc.LastName = 'accLast';
acc.RecordTypeId = rtSA.Id;
acc.PersonEmail = 'tester@test.com';
insert acc;
RecordType rtCUA = [Select Id from RecordType Where Name = 'College/University Account'];
Account acc1 = new Account();
acc1.Name = 'AccName';
acc1.RecordTypeId = rtCUA.Id;
insert acc1;
Qualification__c qual = new Qualification__c();
qual.Name = 'Test Qual';
qual.LMS_Code__c = '1';
qual.Account__c = acc1.Id;
insert qual;
Qualification_Unit__c u = new Qualification_Unit__c();
u.Qualification__c = qual.Id;
u.Name = 'UnitName';
insert u;
RecordType rtSO = [Select Id from RecordType Where Name = 'Student Opportunity'];
Opportunity opp = new Opportunity();
opp.Name = 'Test_Student_Opp';
opp.AccountId = acc.Id;
opp.RecordTypeId = rtSO.Id;
opp.StageName = 'In Progress';
opp.CloseDate = Date.today();
opp.Related_Course__c = qual.Id;
opp.Education_Provider_Account__c = acc1.Id;
opp.CloseDate = date.today()+30;
insert opp;
map<Id,Enrolment__c> mapId = new map<Id,Enrolment__c>();
Enrolment__c en = new Enrolment__c();
en.Opportunity__c = opp.Id;
en.Account__c = acc.Id;
en.Start_Date__c = Date.today();
insert en;
mapId.put(en.Id,en);
Test.startTest();
// This causes a fake response to be generated
Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
// Call the method that invokes a callout
EnrollmentUtil.createNewEnrollment(mapId.keySet());
Test.stopTest();
}
public class MockHttpResponseGenerator implements HttpCalloutMock {
// Implement this interface method
public HTTPResponse respond(HTTPRequest req) {
// Create a fake response
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
res.setBody('{"id":1,"html_url":"www.blah.com"}');
res.setStatusCode(200);
return res;
}
}
}
As I said this was previously passing and no change that would impact this has been made since, except for Salesforce releases. The startTest and stopTest flags look to be placed correctly to separate DML statements from the callout, I can't see any DML statements prior to the callout in the method, and in sandbox I've tried removing the @future tag with no change. Could Winter 16 have broken this?