function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Dhananjay Patil 12Dhananjay Patil 12 

Cron Trigger returns the information about existing Schedule Job in Org while running test class


I am having problem while running below class:
 
@isTest
public with sharing class HandlerTest {

 @isTest static void testScheduleTaskBatch() {
    // Get the information from the CronTrigger API object
    List<CronTrigger> ctBefore = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE CronJobDetail.Name ='SheduledSendTaskBatch' AND State != 'DELETED'];

    System.debug('### ctBefore: ' + ctBefore);

    System.assertEquals(0, ctBefore.size());

    Test.startTest();
      Handler.scheduleTaskBatch(0);
    Test.stopTest();

      List<CronTrigger> ctAfter = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE CronJobDetail.Name = 'SheduledSendTaskBatch' AND State != 'DELETED'];
      System.assertEquals(1, ctAfter.size());
  }

}

It throws below error:

Class HandlerTest Method Name testScheduleTaskBatch Pass/Fail Fail Error Message System.AssertException: Assertion Failed: Expected: 0, Actual: 1 Stack Trace Class.HandlerTest.testScheduleTaskBatch: ' System.assertEquals(0, ctBefore.size());' on this line

I face this assertion error only when if there is any existing Apex Job of name 'SheduledSendTaskBatch' is running in my Org.
But As per my understanding during test class execution the query should return only the batch job that i created in my test class.
I created a Batch Job in Developer console(For Testing purpose in order to debug the issue) like this:
 
SendCMDOTaskBatch m = new SendCMDOTaskBatch();

String sch = '30 0 0 22 12 ?';

String jobID = system.schedule('SheduledSendTaskBatch', sch, m);

at the same time if I run my test class then it throws the assertion error.
Can someone please help how can I avoid this situation?
Best Answer chosen by Dhananjay Patil 12
Jared M NeuferJared M Neufer
I faced a similar problem recently and here's the approach I ended up taking:

The first thing I would do is change how you're querying for the schedulable job.  I would have the Handler method to schedule the class return the jobId. Then, in your query after the Test.stopTest(), use the jobId instead of the Name of the Schedulable class.  This means, you would have no need for an assertion before the test, because the id is unique to the particular schedulable you are creating.

However, this introduces one more issue.  If the job is already scheduled in the org, the test will throw an error for trying to schedule a job that's already scheduled.  Therefore, before the Test.startTest(), unschedule the current job with that name. 
 
@isTest
public with sharing class HandlerTest {

 @isTest static void testScheduleTaskBatch() {
    // Get the information from the CronTrigger API object
    List<CronTrigger> ctBefore = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE CronJobDetail.Name ='SheduledSendTaskBatch' AND State != 'DELETED'];
    if (!ctBefore.isEmpty()){
      System.abortJob(ctBefore[0].Id);
    }
    
    Test.startTest();
      Id result = Handler.scheduleTaskBatch(0);
    Test.stopTest();

      List<CronTrigger> ctAfter = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE Id = :result AND State != 'DELETED'];
      System.assertEquals(1, ctAfter.size());
  }

}

 

All Answers

Jared M NeuferJared M Neufer
I faced a similar problem recently and here's the approach I ended up taking:

The first thing I would do is change how you're querying for the schedulable job.  I would have the Handler method to schedule the class return the jobId. Then, in your query after the Test.stopTest(), use the jobId instead of the Name of the Schedulable class.  This means, you would have no need for an assertion before the test, because the id is unique to the particular schedulable you are creating.

However, this introduces one more issue.  If the job is already scheduled in the org, the test will throw an error for trying to schedule a job that's already scheduled.  Therefore, before the Test.startTest(), unschedule the current job with that name. 
 
@isTest
public with sharing class HandlerTest {

 @isTest static void testScheduleTaskBatch() {
    // Get the information from the CronTrigger API object
    List<CronTrigger> ctBefore = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE CronJobDetail.Name ='SheduledSendTaskBatch' AND State != 'DELETED'];
    if (!ctBefore.isEmpty()){
      System.abortJob(ctBefore[0].Id);
    }
    
    Test.startTest();
      Id result = Handler.scheduleTaskBatch(0);
    Test.stopTest();

      List<CronTrigger> ctAfter = [select Id, CronExpression, TimesTriggered, NextFireTime, State from CronTrigger WHERE Id = :result AND State != 'DELETED'];
      System.assertEquals(1, ctAfter.size());
  }

}

 
This was selected as the best answer
Dhananjay Patil 12Dhananjay Patil 12
It throws an error:
'Illegal assignment from void to Id at line 12'.
I see that in IF condition you are trying to abort the existing job if it returns a value.But If the above code runs in PROD and while running test class,if any exisitng job found then I am afraid it directly abort the job in order to run test class.I am looking for solution where we can at least by pass the the test class even if a test class runs.
Jared M NeuferJared M Neufer
You will need to update your functional code to return the job Id for this to work.

Also, aborting the job from your test class will not affect the schedulable in production since you are running it from a test class.
Dhananjay Patil 12Dhananjay Patil 12
Thanks.This works for me.There is another thing that I have found out,Create a custom setting and the Batch Job Name in the property.Define a final varial in Apex class of type string and in test class Change the value of that variable.This is also works for me.Thank You so much for the suggestion.