I am facing weird behavior of test running.
I have two sandboxes and production. One sandbox is where development is done, second to test. I developed class and deployed it to second sandbox, run all tests and all are passed. This second sandbox is refreshed today, so no difference between it and production. When I tried to deploy codebase to production, I am having CalloutException
, There is no DML before callout
System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out Stack Trace: Class.APICalloutHelper.getResponse: line 13, column 1 Class.Product1APIDaily.doCakeAPIDaily: line 4, column 1 Class.ExternalCakeReports.doCakeAPIDaily: line 14, column 1
Such exception isn't thrown neither on first, or second sandboxes. Here are source test-class
@isTest
private class ExternalCakeReportsTest {
@isTest
static void testSchedule(){
TestData setupData = new TestData();
System.runAs(setupData.testUser) {
setupData.initCutomSettings();
String cronExp = '0 0 1 * * ?';
Test.startTest();
Test.setMock(HttpCalloutMock.class, new ExternalCakeReportsMock());
String actualJobId = System.schedule('Test ExternalCakeReports Test',cronExp, new ExternalCakeReports());
Test.stopTest();
CronTrigger actual = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE id = :actualJobId];
System.assertEquals(cronExp, actual.CronExpression, 'Incorrect Crom Expression');
}
}
public class ExternalCakeReportsMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest req) {
HTTPResponse res = new HTTPResponse();
res.setHeader('Content-Type', 'text/json');
res.setBody('{"data":[], "error":"Test error"}');
res.setStatusCode(200);
return res;
}
}
public class TestData {
public User testUser {get; set;}
public TestData(){
testUser = TestUserData.getUserForTest()
.setProfile(ProfileUtilities.ProfileType.INTEGRATION)
.save()
.getRecord();
}
public void initCutomSettings(){
insert new CakeAPI__c(
Name='Product1',
Endpoint__c = 'http://oberegovskyi.com',
APIKey__c = 'sdfdsfDSFFdfdf'
);
insert new CakeAPI__c(
Name='Product2',
Endpoint__c = 'http://oberegovskyi.com',
APIKey__c = 'sdfdsfDSFFdfdfdfdf'
);
}
}
}
and class
global with sharing class ExternalCakeReports implements Schedulable, Database.AllowsCallouts {
global void execute (SchedulableContext ctx) {
doCakeAPIDaily();
doCakeAPIMonthly();
doAdvertiserReport();
doGetProduct1AccountStatuses();
doGetProduct2AccountStatuses();
}
@future(callout=true)
private static void doCakeAPIDaily() {
Product1APIDaily daily = new Product1APIDaily();
daily.doCakeAPIDaily();
}
@future(callout=true)
private static void doCakeAPIMonthly() {
if(Date.today().day() == 1){
Product1APIMonthly monthly = new Product1APIMonthly();
monthly.doCakeAPIMonthly();
SalesGoalsUpdater goalsUpdater = new SalesGoalsUpdater();
goalsUpdater.doUpdate();
}
}
@future(callout=true)
private static void doAdvertiserReport(){
Date dateYesterday = Date.today() - 1;
String advertiserId= '0';
CDAdvertiserReport adv = new CDAdvertiserReport(dateYesterday, dateYesterday, advertiserId);
adv.callAdvertiserReport();
}
@future(callout=true)
private static void doGetProduct1AccountStatuses() {
CakeStatus status = new CakeStatus(CsApiTypes.Product.Product1);
status.updateStatuses();
}
@future(callout=true)
private static void doGetProduct2AccountStatuses() {
CakeStatus status = new CakeStatus(CsApiTypes.Product.Product2);
status.updateStatuses();
}
}