It seems that System.Formula.recalculateFormulas method triggers unexpected behaviour during apex unit tests. Has anyone faced this issue or has any explanation why this is happening? Logs do not show any DML operations, but if I use Formula.recalculateFormulas right before a callout, it will error in tests with System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out. But in the code, it works perfectly well. Code example below:
public class CalloutsForTest {
final Account account;
CalloutsForTest(Id accountId) {
this.account = [SELECT Id FROM Account WHERE Id = :accountId];
}
public static void doStatelessCallout(Id accountId){
CalloutsForTest calloutObject = new CalloutsForTest(accountId);
calloutObject.doStatefulCallout();
}
public void doStatefulCallout() {
//If recalculateFormulas method (17-21 lines) is recalculated, then test does not fail
{
System.Formula.recalculateFormulas(new List<Account>{
account
});
}
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('GET');
request.setEndpoint('https://www.google.com/');
HttpResponse response;
response = http.send(request);
if (response.getStatusCode() != 200) {
throw new CalloutException(response.getBody());
}
}
}
@IsTest
private class CalloutTest {
@IsTest
static void test() {
//If we remove test data preparation DML formula recalculation method do not break unit test
Id accountId = Database.insert(new Account(Name = 'Test inc.')).getId();
Test.startTest();
Test.setMock(HttpCalloutMock.class, new HttpMock());
CalloutsForTest.doStatelessCallout(accountId);
Test.stopTest();
System.assert(true);
}
public class HttpMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest request) {
HttpResponse response = new HttpResponse();
response.setStatusCode(200);
return response;
}
}
}
Quick summary to reproduce:
- Create custom object with a formula field dependant on two any other fields
- Write a method you will test that changes one of the fields that affect the formula field. Right after you modify the sObject field, call Formulas.recalculateFormula to obtain the value updated in the formula (no DML should happen here, at least it doesn't show in the logs).
- Right after the Formula.recalculateFormulas, do a callout anywhere. Sample scenario would be to use the updated value from the formula to charge a credit card. In theory up to here, you should have not made any DMLs.
- In your test's setup, store an initial object for testing.
- In the test, call your method to change the field's value.
- Run the test. You will see the DML callout error, even though there is no DML ops performed.
- Remove the Formula.recalculateFormulas and the DML callout error disappears. Again, this only happens when running tests and not the prod execution itself
recalculateFormulas
generates DML. The Community will need something it can reproduce