Trying to figure out how to test error condition in the finish()
method of this Batchable class. I am testing the error condition in the execute() method by calling it separately in the test and passing null
for the BatchableContext
parameter. But the finish()
method uses the BatchableContext
. Is there a way to mock the BatchableContext
to test the error condition?
OregonDistrictsBatch.apxc
:
global class OregonDistrictsBatch implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful{
// instance member to retain state across transactions
global Integer recordsProcessed = 0;
// errors stored here for passing to user email in finish method
Exception[] errors = new Exception[0];
string stringErrors = '';
global Boolean apiLimitExceeded = false;
// query requires oregon street address, active wkr w/PIN or CW record,
// and null in any one of the 4 fields populated by the callout.
// for now also including records with 0s in all district fields
// but can remove that once error handling for over API limits is implemented
string q = 'SELECT Id,MailingStreet,MailingCity,MailingState,MailingPostalCode FROM Contact WHERE MailingStreet != NULL AND MailingState=\'OR\' AND ((Active_Worker__c = TRUE AND L503_PIN__c != NULL) OR Has_CareWorks_Record__c = TRUE) AND (District_Update_Date__c = NULL OR Cong_District__c = NULL OR OR_HD__c = NULL OR OR_SD__c = NULL)';
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator(q);
}
global void execute(Database.BatchableContext BC, List<Contact> scope){
// process each batch of records
try {
for ( Contact c : scope) {
String address = EncodingUtil.urlEncode(c.MailingStreet + ','
+ c.MailingCity + ' '
+ c.MailingState + ' '
+ c.MailingPostalCode, 'UTF-8');
system.Debug('Address =' + address);
oregonDistricts od = new oregonDistricts(address);
if (od.apiLimitExceeded != true) {
c.Cong_District__c = od.congressionalDistrict;
c.OR_HD__c = od.statehouseDistrict;
c.OR_SD__c = od.statesenateDistrict;
c.District_Update_Date__c = system.now();
} else {
apiLimitExceeded = true;
stringErrors = 'API limit exceeded';
return;
}
recordsProcessed = recordsProcessed + 1;
}
update scope;
} catch (Exception e) {
errors.add(e);
}
}
global void finish(Database.BatchableContext BC){
System.debug(recordsProcessed + ' records were processed.');
// Get the ID of the AsyncApexJob representing this batch job
// from Database.BatchableContext.
// Query the AsyncApexJob object to retrieve the current job's information.
AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,
TotalJobItems, CreatedBy.Email
FROM AsyncApexJob WHERE Id =
:BC.getJobId()];
// Send an email to the Apex job's submitter with list of errors, if any.
String msg='';
for (integer i =0; i<errors.size();i++) {
msg +='\n' +'Error: ' + errors[i];
}
if(!errors.isEmpty() || stringErrors != '') {
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setSubject('Errors occurred during batch process.');
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setPlainTextBody(msg);
Messaging.sendEmail(new Messaging.Email[] { mail });
}
// Send an email to the Apex job's submitter notifying of job completion.
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Sharing Recalculation ' + a.Status);
mail.setPlainTextBody
('The batch OregonDistricts job processed ' + recordsProcessed + ' in ' + a.TotalJobItems +
' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
the uncovered code I'm trying to test is this block in the finish()
method: if(!errors.isEmpty() || stringErrors != '') { ... }
This is the test that's working to test the error condition in the execute()
method:
static testmethod void testErrorCondition() {
List<Contact> contacts = new List<Contact>();
// insert 10 contacts with unknown employer and test address; these will be guaranteed to meet query criteria for queriedContacts List
for (Integer i=0;i<10;i++) {
contacts.add(new Contact(
firstname='first' + i,
lastname='last' + i,
accountId='0016100001RyWhAAAV',
Has_CareWorks_Record__c = TRUE,
MailingStreet='3892 friar ct se',
MailingCity='Salem',
MailingState='OR',
MailingPostalCode='97302'));
}
insert contacts;
List<Contact> queriedContacts = [SELECT Id,MailingStreet,MailingCity,MailingState,MailingPostalCode FROM Contact WHERE MailingStreet != NULL AND (District_Update_Date__c = null OR Cong_District__c = null OR OR_HD__c = null OR OR_SD__c = null)];
Test.setMock(HttpCalloutMock.class, new OregonDistrictsErrorMock());
Test.startTest();
OregonDistrictsBatch odb = new OregonDistrictsBatch();
odb.execute(null, queriedContacts);
Test.stopTest();
// after the testing stops, assert that 'apiLimitExceeded' var is set to true
// (this test simulates a callout after daily API limits are met)
System.assertEquals(odb.apiLimitExceeded, true);
}
But I need a separate test for the finish()
method to test the error condition there and can't figure out how to do it because I need to pass BatchableContext
as a parameter.
A commenter on another post suggested using a dependency injection but I don't know how to do that here.