Most of us have a morning routine, whether it's waking up early and making breakfast for the entire family and leaving for work or waking up 5 minutes before you're supposed to leave and have coffee as breakfast and leaving in rush. There are certain activities that need to happen on a regular basis and even at work, it could be sending an email with today's update on opportunities, or monthly revenue stats or annual reports on how the company is performing. To automate our code, we use Scheduled Apex.

As the name suggests, Scheduled Apex runs a task at specific time frames as defined by the developer. A Scheduled Class implements Schedulable and has an execute() method. Let's take a look at the syntax:

global class SomeClass implements Schedulable {
                global void execute(SchedulableContext ctx) {
                    //Code Here
                }
            }
  • A schedule class is always global
  • The class must implement Schedulable
  • The Class must have an execute() method.
  • The SchedulableContext ctx is just like the Batch Class' Database.BatchableContext bc, it is the jobID of the schedule class.

It's good practice to call your Batch Class / Apex Class from your Schedule Class instead of filling it up with code. Let's call a Batch Class:

global class ClassSchedule implements Schedulable {
                global void execute(SchedulableContext ctx) {
                    //Code Here
                    BatchClass tester = new BatchClass();
                    Database.executeBatch(tester);
                }
            }

We made a new class called ClassSchedule that implements Schedulable and called our batch class from the execute() method. Nice and simple. Now let's schedule it.

When scheduling the class, it runs on the user's time zone that's executing it, and for most parts that's the developer's time zone.

To schedule a class, we first make a new instance.

ClassSchedule schClass = new ClassSchedule();

Now, we setup time using a very specific format. Seconds Minutes Hours Day_Of_Month Month Day_Of_Week Optional_Year.

Here's the markup on values:

Name Values SpecialCharacters
Seconds 0–59 None
Minutes 0–59 None
Hours 0–23 None
Day_of_month 1–31 , - * ? / L W
Month 1–12 or First three letters of months (JAN - DEC) , - * /
Day_of_week 1–7 or First three letters of the day (SUN - SAT) , - * ? / L #
optional_year null or 1970–2099 , - * /

Break down of special characters:

Special Character Description
, Delimits values. For example, use JAN, MAR, APR to specify more than one month.
- Specifies a range. For example, use JAN-MAR to specify more than one month.
* Specifies all values. For example, if Month is specified as *, the job is scheduled for every month.
? Specifies no specific value. This is only available for Day_of_monthand Day_of_week, and is generally used when specifying a value for one and not the other.
/ Specifies increments. The number before the slash specifies when the intervals will begin, and the number after the slash is the interval amount. For example, if you specify 1/5 for Day_of_month, the Apex class runs every fifth day of the month, starting on the first of the month.
L Specifies the end of a range (last). This is only available for Day_of_month and Day_of_week. When used with Day of month, L always means the last day of the month, such as January 31, February 29 for leap years, and so on. When used with Day_of_week by itself, it always means 7 or SAT. When used with a Day_of_week value, it means the last of that type of day in the month. For example, if you specify 2L, you are specifying the last Monday of the month. Do not use a range of values with L as the results might be unexpected.
W Specifies the nearest weekday (Monday-Friday) of the given day. This is only available for Day_of_month. For example, if you specify 20W, and the 20th is a Saturday, the class runs on the 19th. If you specify 1W, and the first is a Saturday, the class does not run in the previous month, but on the third, which is the following Monday. Use the L and W together to specify the last weekday of the month.
# Specifies the nth day of the month, in the format weekday#day_of_month. This is only available for Day_of_week. The number before the # specifies weekday (SUN-SAT). The number after the # specifies the day of the month. For example, specifying 2#2 means the class runs on the second Monday of every month.

Understanding the tables could be a little difficult, so let's take some examples:

Expression Description
0 0 13 * * ? Class runs every day at 1 PM.
0 0 22 ? * 6L Class runs the last Friday of every month at 10 PM.
0 0 10 ? * MON-FRI Class runs Monday through Friday at 10 AM.
0 0 20 * * ? 2010 Class runs every day at 8 PM during the year 2010.

I am writing this at 3:52AM and I want to schedule my class to run 4AM everyday

String sch = '0 0 4 * * ?';
  • First two values denoting seconds and horus is 0 and the third value is 4 so that means the time is 04:00 or 4AM.
  • The next two values are * denoting we want Day_of_the month and Month to be all values, so 1st through 31st, Jan to Dec.
  • The Day_of_week is a ? denoting we don't want to specify a value because we have already specified values for our day_of_month value so this does not require a value
  • The year is omitted because it's an optional value.

This might take some time to understand and I still refer to this chart and spend a solid minute writing when to run so don't worry if you don't get it right away. Now that we have what and when to run, let's actually execute it. To do this, we use the System.schedule() class method that takes three parameters:

  • Name (String) of the job.
  • Time (String) called a Cron Time.
  • Scheduled Class (variable).

Just like our Batch Class execution, we save the ID in a variable. Let's write it all together in an Anon Exec window:

ClassSchedule classer = new ClassSchedule();
            String sch = '0 0 4 * * ?'; //run everyday at 4
            String jobId = system.schedule('classScheduler', sch, classer);
            System.debug(jobId);

After our code runs, head over to Setup and search for Scheduled Jobs and you will notice there's a new job scheduled for the time you specified! (I ran mine on 3:55AM because I was restless.)

Scheduled Class in Setup

There is a delete option here to delete the scheduled job. It is possible to reschedule the class from code, but I've seen experienced developers delete the job and schedule a new one for the class so it avoids confusion. Now that we know how to write a Schedule Class, let's write Test Class for it.

Test Class

Personally, writing a test class for scheduled class feels like a formality just because most scheduled classes I've written over the last year called a Batch Class and those classes had Tests written already, but since we always aim for 100% code coverage, we just need to call the Scheduled Class once. I've also seen developers follow the same practice.

For this, we need to define a Time string, but call the Scheduled class inside the Test.startTest() and Test.stopTest() because this forces the scheduled class to run immediately on the test database because we really don't want to wait for testing it.

@isTest
            public class ClassScheduleTest {
                public static String sch_test = '0 32 4 * * ?'; //define time

                static testmethod void testerMethod(){

                    Test.startTest();
                    ClassSchedule tester = new ClassSchedule();
                    String jobId = System.schedule('Test Class', sch_test, tester);
                    Test.stopTest();


                }
            }

Scheduled classes do have certain limitations that we will discuss when we look into making HTTP calls from Apex classes.

Summary

  • Scheduled classes are run on the time frame set by the developer.
  • The class always runs based on user's timezone
  • Writing test classes for scheduled classes is mostly redundant and a practical approach is to write a test class for the classes it's calling and a name-sake test for the said scheduled class just to achieve 100% code coverage.
Day 21: Networking and REST API