I can't figure out what I'm doing wrong here. So, I have a batch class, when I call it, it instantiates a new List<Database.SaveResult>() which is non-static and since I'm implementing Database.Stateful I thought that that's all I had to do, but when it gets referenced in the finish() method, it's null!!
My error:
NullPointerException: Attempt to de-reference a null object
BatchablePlacementChecker.finish() line 27
The line that is failing is this (second line of the finish() method):
if (errors.size() < 1 && !Test.isRunningTest()) {return;}
'errors' is the name of my variable, new List<Database.SaveResult>(); which gets instantiated in the constructor when I call the batch class.
The execute() method adds any Database.SaveResult objects to the errors variable if the save result isn't a success. I am collecting them to email them as a CSV to any currently active System Administrators in the finish() method, just so you have the background.
I'm calling this batch class in my test like this:
Database.executeBatch(new BatchablePlacementChecker(), 50);
Here's the full class:
public without sharing class BatchablePlacementChecker implements Database.Batchable<SObject>, Database.Stateful {
public List<Database.SaveResult> errors;
public BatchablePlacementChecker() {
List<Database.SaveResult> errors = new List<Database.SaveResult>();
}
public Database.QueryLocator start(Database.BatchableContext dbc) {
return Database.getQueryLocator('SELECT Id, On_Placement__c, (SELECT Id FROM Placements__r WHERE (Start_Date__c <= TODAY AND End_Date__c > TODAY) OR Confirmed_Start_Date__c <= TODAY) FROM Candidate__c');
}
public void execute(Database.BatchableContext dbc, List<Candidate__c> candidates) {
List<Candidate__c> processed = new List<Candidate__c>();
for (Candidate__c candidate : candidates) {
candidate.On_Placement__c = candidate.Placements__r.size() > 0 ? true : false;
processed.add(candidate);
}
List<Database.SaveResult> results = Database.update(processed, false);
for (Database.SaveResult result : results) {
if (!result.isSuccess()) {errors.add(result);}
}
}
public void finish(Database.BatchableContext dbc) {
sendNotification('Batch Job Completed', 'The BatchablePlacementChecker job has finished.');
if (errors.size() < 1 && !Test.isRunningTest()) {return;}
String csvOfIds = 'ID,Errors\n';
for (Database.SaveResult erroredRecord : errors) {
csvOfIds += erroredRecord.id + ',' + erroredRecord.errors + '\n';
}
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
attachment.setFileName('[' + Date.today() + '] BatachablePlacementCheckerErrors.csv');
attachment.setBody(Blob.valueOf(csvOfIds));
email.setFileAttachments(new Messaging.EmailFileAttachment[]{attachment});
String[] toAddresses = new String[]{};
for (User administrator : [SELECT Id, Email FROM User WHERE Profile.Name = 'System Administrator' AND IsActive = TRUE]) {
toAddresses.add(administrator.Email);
}
email.setToAddresses(new String[]{'[email protected]'}); //Update this to toAddresses for live.
email.setSubject('Errors During BatchablePlacementChecker');
email.setHtmlBody('Some Candidate records failed to save when running the BatachablePlacementChecker class.');
if (!Test.isRunningTest()) {Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});}
}
private void sendNotification(String title, String body) {
CustomNotificationType notificationType = [SELECT Id, DeveloperName FROM CustomNotificationType WHERE DeveloperName = 'Batch_Job'];
Messaging.CustomNotification notification = new Messaging.CustomNotification();
notification.setTitle(title);
notification.setBody(body);
notification.setNotificationTypeId(notificationType.Id);
notification.setTargetId(UserInfo.getUserId());
try {notification.send(new Set<String>{UserInfo.getUserId()});}
catch (Exception e) {System.debug('Problem sending notification: ' + e.getMessage());}
}
}