I'm working on an apex class that does the following:
- On a given record, compress field data from it and four child objects into CSV format on a long text field
- Export the compressed data to files in batches of 1000
The test class I've written (has only one test method) always passes when I run it on its own, but when I've performed a run of multiple test classes it consistently fails due to hitting the CPU time limit. It happens irrespective of whether or not other batch tests are involved.
My understanding is that test methods have independent governor limits, so it perplexes me that other methods running would impact the CPU time. Furthermore, I've nominated my tests to run synchronously I can't see how any other classes would "stall" this one.
The test class is below. The failure happens at the line between startTest() and stopTest().
@isTest
public class ASELCC_Spreadsheet_Generator_test {
public static testMethod void test() {
List<ASELCC_Participant__c> apList = new List<ASELCC_Participant__c>();
List<Assessment__c> aList = new List<Assessment__c>();
List<ASELCC_Subtyping_Questionnaire__c> asqList = new List<ASELCC_Subtyping_Questionnaire__c>();
List<ASELCC_Parent_Transition_Questionnaire__c> apqList = new List<ASELCC_Parent_Transition_Questionnaire__c>();
List<ASELCC_Teacher_Transition_Questionnaire__c> atqList = new List<ASELCC_Teacher_Transition_Questionnaire__c>();
//Populate and insert ASELCC Participants
for (Integer i=1;i<=200;i++)
{
apList.add(new ASELCC_Participant__c(
Name='TEST',
Date_of_birth__c = Date.today() - 1));
}
insert apList;
//Populate and insert Time Points, and Transitional Questionnaires
for(ASELCC_Participant__c ap : apList)
{
//Time points
aList.add(new Assessment__c(Client__c = ap.Id, Time_point__c = 'Entry'));
/*aList.add(new Assessment__c(Client__c = ap.Id, Time_point__c = 'EOY1'));
aList.add(new Assessment__c(Client__c = ap.Id, Time_point__c = 'EOY2'));
aList.add(new Assessment__c(Client__c = ap.Id, Time_point__c = 'Exit'));*/
//Subtyping Questionnaires
asqList.add(new ASELCC_Subtyping_Questionnaire__c(
ASELCC_Participant__c = ap.Id, Time_Point_PL__c = 'Entry'));
//Transitional Questionnaires
apqList.add(new ASELCC_Parent_Transition_Questionnaire__c(
ASELCC_Participant__c = ap.Id,
Time_Point__c = 'Kindergarten'));
//apqList.add(new ASELCC_Parent_Transition_Questionnaire__c(ASELCC_Participant__c = ap.Id,Time_Point__c = 'EOY1'));
atqList.add(new ASELCC_Teacher_Transition_Questionnaire__c(
ASELCC_Participant__c = ap.Id,
Time_Point__c = 'Kindergarten'));
//atqList.add(new ASELCC_Teacher_Transition_Questionnaire__c(ASELCC_Participant__c = ap.Id,Time_Point__c = 'EOY1'));
}
insert aList;
insert asqList;
insert apqList;
insert atqList;
Test.startTest();
insert new Research_Spreadsheet_Request__c();
Test.stopTest();
delete asqList;
delete aList;
delete atqList;
delete apqList;
delete apList;
}
}
The batchable class is:
global with sharing class ASELCCSpreadsheetGeneratorBatchable implements Database.Batchable<SObject>, Database.Stateful {
Research_Spreadsheet_Request__c rsr;
final ASELCC_Spreadsheet_Generator asg;
final String query;
Integer fileNumber = 1;
global ASELCCSpreadsheetGeneratorBatchable(Research_Spreadsheet_Request__c rsr){
this.rsr = rsr;
asg = new ASELCC_Spreadsheet_Generator(rsr);
query = 'select Name, Participant_CSV_Data__c, '+
'T1_Assessment_Data__c, T1_Assessment_Data_subtyping__c,'+
'T2_Assessment_Data__c, T2_Assessment_Data_subtyping__c,'+
'T3_Assessment_Data__c, T3_Assessment_Data_subtyping__c,'+
'EOY3_Assessment_Data_Summary__c, EOY3_Assessment_Data_subtyping__c,'+
'T4_Assessment_Data__c, T4_Assessment_Data_subtyping__c,'+
'Transitional_Data_Parent_KG__c, Transitional_Data_Parent_EOY1__c,'+
'Transitional_Data_Teacher_KG__c, Transitional_Data_Teacher_KG_2__c,'+
'Transitional_Data_Teacher_EOY1__c, Transitional_Data_Teacher_EOY1_2__c '+
'from ASELCC_Participant__c order by Name asc';
}
global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<sObject> scope){
asg.generate(scope, fileNumber++);
}
global void finish(Database.BatchableContext BC){
rsr = [select Id from Research_Spreadsheet_Request__c where Id =: rsr.Id];
rsr.export_status__c = 'Completed';
update rsr;
}
}
The code for the heavy-lifting is:
public class ASELCC_Spreadsheet_Generator{
Research_Spreadsheet_Request__c rsr;
public ASELCC_Spreadsheet_Generator(Research_Spreadsheet_Request__c rsr)
{
this.rsr = rsr;
}
public void generate(List<ASELCC_Participant__c> apList, Integer fileNumber){
String headerRow;
List<String> rows;
String rowData;
//GENERATE CSV FILE
//Create header row - this will vary depending on the user selection
headerRow = addHeaderRow();
//Fetch data, store in list of strings
rows = new List<String>();
for(Integer i=0; i<apList.size(); i++){
//Fetch the condensed data - this will vary depending on the spreadsheet type
rowData = writeToRow(apList[i]);
rows.add(rowData);
}
//CREATE FILE
String finalOutput = headerRow+'\n'+String.join(rows,'\n');
Blob content = Blob.valueOf(finalOutput);
finalOutput = '';
Attachment csv = new Attachment(
//File name has ASELCC, then the date, then the batch number if more than one file was generated
Name='ASELCC_'+rsr.Name+'_'+Date.today().format() +
((fileNumber > 1) ? (' - ' + fileNumber.format()) : '') +'.csv',
ContentType='text/csv',
Body=content,
parentId=rsr.Id
);
insert csv;
}
String writeToRow(ASELCC_Participant__c ap){
//Fetch the condensed data - this will vary depending on the spreadsheet type
String data = '';
if (rsr.Participant_Demographic_Details__c) data += ap.Participant_csv_data__c + ',';
else data += ap.Name + ',';
if (rsr.T1_Summary_Data__c) data += ap.t1_assessment_Data__c + ',';
if (rsr.T1_Raw_Data__c) data += ap.t1_assessment_Data_subtyping__c + ',';
if (rsr.T2_Summary_Data__c) data += ap.t2_assessment_Data__c + ',';
if (rsr.T2_Raw_Data__c) data += ap.t2_assessment_Data_subtyping__c + ',';
if (rsr.T3_Summary_Data__c) data += ap.t3_assessment_Data__c + ',';
if (rsr.T3_Raw_Data__c) data += ap.t3_assessment_Data_subtyping__c + ',';
if (rsr.EOY3_Summary_Data__c) data += ap.EOY3_assessment_Data_summary__c + ',';
if (rsr.EOY3_Subtyping_Data__c) data += ap.EOY3_assessment_Data_subtyping__c + ',';
if (rsr.T4_Summary_Data__c) data += ap.t4_assessment_Data__c + ',';
if (rsr.T4_Raw_Data__c) data += ap.t4_assessment_Data_subtyping__c + ',';
if (rsr.Trans_Questionnaire_Parent_Kinder__c) data += ap.Transitional_Data_Parent_KG__c + ',';
if (rsr.Trans_Questionnaire_Teacher_Kinder__c ) data += ap.Transitional_Data_Teacher_KG__c + ',';
if (rsr.Trans_Questionnaire_Teacher_Kinder_2__c ) data += ap.Transitional_Data_Teacher_KG_2__c + ',';
if (rsr.Trans_Questionnaire_Parent_EOY1__c) data += ap.Transitional_Data_Parent_EOY1__c + ',';
if (rsr.Trans_Questionnaire_Teacher_EOY1__c) data += ap.Transitional_Data_Teacher_EOY1__c + ',';
if (rsr.Trans_Questionnaire_Teacher_EOY1_2__c) data += ap.Transitional_Data_Teacher_EOY1_2__c;
return data;
}
String addHeaderRow(){
String header = '';
if (rsr.Participant_Demographic_Details__c) header += ASELCCPartController.addHeader_participant() + ',';
else header += 'Child\'s ASELCC Id,';
if (rsr.T1_Summary_Data__c) header += ASELCCAssessController.addHeaderSection_TnAssessment('Entry') + ',';
if (rsr.T1_Raw_Data__c) header += ASELCCSubtypingQuestionnaireController.addHeaderSection_SubTypingAssessment('Entry') + ',';
if (rsr.T2_Summary_Data__c) header += ASELCCAssessController.addHeaderSection_TnAssessment('EOY1') + ',';
if (rsr.T2_Raw_Data__c) header += ASELCCSubtypingQuestionnaireController.addHeaderSection_SubTypingAssessment('EOY1') + ',';
if (rsr.T3_Summary_Data__c) header += ASELCCAssessController.addHeaderSection_TnAssessment('EOY2') + ',';
if (rsr.T3_Raw_Data__c) header += ASELCCSubtypingQuestionnaireController.addHeaderSection_SubTypingAssessment('EOY2') + ',';
if (rsr.eoy3_summary_data__c) header += ASELCCAssessController.addHeaderSection_TnAssessment('EOY3') + ',';
if (rsr.eoy3_subtyping_data__c) header += ASELCCSubtypingQuestionnaireController.addHeaderSection_SubTypingAssessment('EOY3') + ',';
if (rsr.T4_Summary_Data__c) header += ASELCCAssessController.addHeaderSection_TnAssessment('Exit') + ',';
if (rsr.T4_Raw_Data__c) header += ASELCCSubtypingQuestionnaireController.addHeaderSection_SubTypingAssessment('Exit') + ',';
if (rsr.Trans_Questionnaire_Parent_Kinder__c) header += ASELCCParentTransController.addHeaderSection('KG') + ',';
if (rsr.Trans_Questionnaire_Teacher_Kinder__c ) header += ASELCCTeacherTransController.addHeaderSection('KG-1') + ',';
if (rsr.Trans_Questionnaire_Teacher_Kinder_2__c ) header += ASELCCTeacherTransController.addHeaderSection('KG-2') + ',';
if (rsr.Trans_Questionnaire_Parent_EOY1__c) header += ASELCCParentTransController.addHeaderSection('EOY1') + ',';
if (rsr.Trans_Questionnaire_Teacher_EOY1__c) header += ASELCCTeacherTransController.addHeaderSection('EOY1-1') + ',';
if (rsr.Trans_Questionnaire_Teacher_EOY1_2__c) header += ASELCCTeacherTransController.addHeaderSection('EOY1-2') + ',';
return header;
}
}
Thanks in advance!
SeeAllData=true
?