From the top, I do understand how "You have uncommitted work pending" errors happen, but the following is very strange behaviour from Salesforce...
It seems that for one org we have, creating an OpportunityContactRole before running a batch class which performs a callout causes an "uncommitted work pending" error. I've tried reproducing in a sandbox, and it doesn't happen there. The same code works if there is an Account inserted instead of an OCR before starting the batch.
I've tried disabling other automation in the org where this is happening, but still no luck. Although, really, I'd expect that other processes should have no effect anyway as the transactions should be isolated.
I've checked through the debug logs and confirmed that there's only one instance of DML_BEGIN in each, and nothing unexpected firing off.
Any idea what could cause this?
My code follows...
The batch class:
public with sharing class BatchCallout implements Database.Batchable<SObject>, Database.AllowsCallouts {
public void execute(Database.BatchableContext param1, List<Object> param2) {
HttpRequest req = new HttpRequest();
req.setEndpoint(Url.getOrgDomainUrl().toExternalForm() + '/services/data/v44.0/limits');
req.setMethod('GET');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
new Http().send(req);
}
public void finish(Database.BatchableContext param1) {}
public Iterable<SObject> start(Database.BatchableContext param1) {
return (Iterable<SObject>)new List<Account>{new Account()};
}
}
The test class:
@IsTest
private class BatchCalloutTest {
private class CalloutMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest param1) {
return new HttpResponse();
}
}
@TestSetup
static void setup() {
Opportunity testOpportunity = new Opportunity(
Name = 'Test Opportunity 1',
CloseDate = Date.today().addDays(30),
StageName = 'Qualified Enquiry'
);
insert testOpportunity;
Contact testContact = new Contact(
FirstName = 'First ' + String.valueOf(Datetime.now().getTime()),
LastName = 'Last ' + String.valueOf(Math.random() * 10000)
);
insert testContact;
}
static {
Test.setMock(HttpCalloutMock.class, new CalloutMock());
}
@IsTest
static void withoutOpportunityContactRole() {
insert new Account(Name = 'Just some DML');
Test.startTest();
Database.executeBatch(new BatchCallout());
Test.stopTest();
}
@IsTest
static void withOpportunityContactRole() {
insert new OpportunityContactRole(
OpportunityId = [SELECT Id FROM Opportunity].Id,
ContactId = [SELECT Id FROM Contact].Id,
IsPrimary = false
);
Test.startTest();
Database.executeBatch(new BatchCallout());
Test.stopTest();
}
}
Test.startTest()
andTest.stopTest()
. Maybe it'd be worth trying to call yourexecute()
method directly (your test shouldn't need to test that Salesforce's Batchable interface works correctly, that's Salesforce's responsibility).#whyadminsdrink
- I'm with Derek and if you have the inclination, pursue a support case