I'm having trouble figuring out why, when executing Test Class, which fires a Scheduled Class and the Batch Class within it, data I expect to be there, isn't.
@isTest static void test_scheduleExecution() {
Test.startTest();
Date tomorrow = Date.today().addDays(1);
String day = String.valueOf(tomorrow.day());
if (day.length() == 1) {
day = '0' + day;
}
String month = String.valueOf(tomorrow.month());
if (month.length() == 1) {
month = '0' + month;
}
String year = String.valueOf(tomorrow.year());
String CRON_EXP = '0 0 0 ' + day + ' ' + month + ' ? ' + year + '';
String jobId = System.schedule('ScheduleGetJobBoardActivity', CRON_EXP, new ScheduleGetJobBoardActivity());
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE id = :jobId];
System.assertEquals(0, ct.TimesTriggered);
System.assertEquals('' + year + '-' + month + '-' + day + ' 00:00:00', String.valueOf(ct.NextFireTime));
Job_Board_Activity__c[] activity = [SELECT Id FROM Job_Board_Activity__c];
System.assertEquals(0, activity.size());
Test.stopTest();
activity = [SELECT Id FROM Job_Board_Activity__c];
System.assertEquals(3, activity.size());
}
Long story short with this method: I'm scheduling the job, checking nothing's happened with it (e.g. it's not firing early and creating records before it should), but after Test.stopTest();
I'd expect the job to actually fire and there should be 3 records within the Job_Board_Activity__c
object.
I know the Batch class is running correctly, because the finish()
method is being hit and the for
loop is looping through those records, but it isn't executing the INSERT
request:
I checked the debug log and confirmed there are items in the List<>
, so why wouldn't they be inserted?
Note: The scheduled class looks at records in Metadata types, and execute multiple batch classes depending on how many are in there, the Scheduled class is below:
global class ScheduleGetJobBoardActivity implements Schedulable {
global void execute(SchedulableContext sc) {
for (Job_Board_Activity_Setting__mdt activity : [SELECT Id, Label, Object_API_Name__c, Source_Relationship__c FROM Job_Board_Activity_Setting__mdt WHERE Active__c = true]) {
BatchGetJobBoardActivity b = new BatchGetJobBoardActivity(activity.Object_API_Name__c, activity.Source_Relationship__c);
Database.executeBatch(b);
}
}
}
Each iteration should insert 1 record over 3 loops, so 3 records in total.
Note 2: Coverage is fine on this, 100% on both classes. I'm just wondering why the asserts won't work?
Edit: The only thing I can think of now is that because the Batch class executes within a loop, the last loop doesn't have anything to insert and that's what the test class 'remembers'? But this is just a guess. I'd have though it would only 'forget' things outside of the Unit Test's method?
Edit 2:
Batch Class added for clarity:
global class BatchGetJobBoardActivity implements Database.Batchable<sObject>, Database.Stateful {
global String objectAPIName;
global String fieldAPIName;
global static final Date DEFAULT_DATE = Date.today().addDays(-1);
global Datetime dateToProcess;
String query;
global Map<Id, List<Id>> sourceToListOfRecords = new Map<Id, List<Id>>();
global BatchGetJobBoardActivity(String obj, String fld) {
this.objectAPIName = obj;
this.fieldAPIName = fld;
this.dateToProcess = DEFAULT_DATE;
}
global BatchGetJobBoardActivity(String obj, String fld, Date d) {
this.objectAPIName = obj;
this.fieldAPIName = fld;
this.dateToProcess = d;
}
global Database.QueryLocator start(Database.BatchableContext BC) {
query = 'SELECT Id FROM ' + objectAPIName + ' WHERE CreatedDate > ' + dateToProcess.format('yyyy-MM-dd') + 'T00:00:00Z AND CreatedDate < ' + dateToProcess.format('yyyy-MM-dd') + 'T23:59:59Z';
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<sObject> scope) {
String queryRecords;
queryRecords = 'SELECT Id, ' + fieldAPIName + ' FROM ' + objectAPIName + ' WHERE Id IN :scope AND ' + fieldAPIName + ' != null';
for (sObject sObj : Database.query(queryRecords)) {
List<Id> l = new List<Id>();
String[] fieldAPINameParse = fieldAPIName.split('\\.');
/*
Start: Tidy this part up!
*/
if (fieldAPINameParse.size() == 1) {
l = sourceToListOfRecords.get((Id)sObj.get(fieldAPINameParse[0]));
}
else if (fieldAPINameParse.size() == 2) {
l = sourceToListOfRecords.get((Id)sObj.getSObject(fieldAPINameParse[0]).get(fieldAPINameParse[1]));
}
else if (fieldAPINameParse.size() == 3) {
l = sourceToListOfRecords.get((Id)sObj.getSObject(fieldAPINameParse[0]).getSObject(fieldAPINameParse[1]).get(fieldAPINameParse[2]));
}
if (l == null && fieldAPINameParse.size() == 1) {
l = new List<Id>();
sourceToListOfRecords.put((Id)sObj.get(fieldAPIName), l);
}
else if (l == null && fieldAPINameParse.size() == 2) {
l = new List<Id>();
sourceToListOfRecords.put((Id)sObj.getSObject(fieldAPINameParse[0]).get(fieldAPINameParse[1]), l);
}
else if (l == null && fieldAPINameParse.size() == 3) {
l = new List<Id>();
sourceToListOfRecords.put((Id)sObj.getSObject(fieldAPINameParse[0]).getSObject(fieldAPINameParse[1]).get(fieldAPINameParse[2]), l);
}
/*
Finish: Tidy this part up!
*/
l.add(sObj.Id);
}
}
global void finish(Database.BatchableContext BC) {
Date batchDate = Date.valueOf(dateToProcess);
List<Job_Board_Activity__c> jList = new List<Job_Board_Activity__c>();
for (Id source : sourceToListOfRecords.keySet()) {
Job_Board_Activity__c j = new Job_Board_Activity__c(
Count__c = sourceToListOfRecords.get(source).size(),
Date__c = batchDate,
Object_Name__c = objectAPIName,
Source__c = source
);
jList.add(j);
}
INSERT jList;
}
}
Edit 3: So I wrote another method which manually executes the Batch method instead of relying on the Scheduler:
Test.startTest();
BatchGetJobBoardActivity b = new BatchGetJobBoardActivity('ts2__Application__c', 'ts2__Application_Source__c', Date.today().addDays(-1));
Database.executeBatch(b);
Test.stopTest();
Integer i = [SELECT COUNT() FROM Job_Board_Activity__c];
System.assertEquals(1, i);
The parameters are:
- Object API name
- Field API name
- 'Collect records created on this date'
This works, the object and field API names are all that's stored in the MetaData which is what the scheduled class would iterate over, passing them in as Strings. I can only assume the problem is with the Scheduled class and not the Batch class as a result.
sourceToListOfRecords
. If it's size is zero, you won't have any records to insert forjList
. Can add the same system.debug statments to that for loop in the finish method as well. I don't see a SeeAllData, so not certain if you can execute that query without importing test data.System.debug('List size: ' + jList.size());
in theglobal void finish()
method and saw that all the MetaData types are executing. There are 8 in total, five returned 0, three returned 1. This is what I expected. I have put in test data in a@testSetup
method and not usedseeAllData
. It seems to know there's 1 record to insert, but it's not actually doing it?