I have the following class which implements the 'schedulable' interface and need help to write a test class to complete the code coverage please:

the class checks if the campaign start date is current date and if so extracts all the campaigns and sends an email:

  public class CampaignMailOnStartDate implements Schedulable{
    public static void getStartdateToday(List<Campaign> campaign){
    String objectName = 'Campaign';
    String query = 'SELECT';

    //Map<String, Schema.SObjectField> objectFields = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap();
    Map <String, Schema.SObjectField> objectFields = Campaign.getSobjectType().getDescribe().fields.getMap();
    for (SObjectField fld : objectFields.values()) {
        string fld_str = string.valueOf(fld);
        query += ' ' + fld + ', ';
        /*if (fld_str == 'id'            || fld_str == 'name'      || fld_str == 'parentid'    ||
            fld_str == 'status'      || fld_str == 'startdate' || fld_str == 'enddate'     ||
            fld_str == 'description' || fld_str == 'ownerid'   || fld_str == 'createddate' ||
            fld_str == 'createdbyid')
        query += ' ' + fld + ', ';*/
    // Strip off the last comma if it exists.
    if (query.subString(query.Length()-2,query.Length()) == ', '){
        query = query.subString(0,query.Length()-2);

    // Add FROM statement
    query += ' FROM ' + objectName;
    query += ' WHERE StartDate = TODAY'; 

    try {   
            List<Campaign> campaignList = new List<Campaign>();
            campaignList = database.query(query);
            for(Campaign c : campaignList){
        } catch (QueryException e){
            //perform exception handling
            system.debug('Error in Query');

public static void sendMail(List<Campaign> Campaigns, EmailTemplate template ){
    List< Messaging.SingleEmailMessage> emails = new List< Messaging.SingleEmailMessage>();
    for(Campaign camp:campaigns){
        //New instance of a single email message
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        // Set mail template
        // Set target id
        // Set mail body
        String htmlBody = template.HtmlValue; 
        User createdBy = [select name from user where id =: camp.CreatedById limit 1]; 
        htmlBody = htmlBody.replace('{!Campaign.CreatedBy}', createdBy.Name);
        htmlBody = htmlBody.replace('{!Campaign.Name}', camp.Name);
        htmlBody = htmlBody.replace('{!Campaign.Description}', camp.Description);

        //htmlBody = htmlBody.replace('{!Campaign.StartDate}', camp.StartDate);
        htmlBody = htmlBody.replace('{!Campaign.Status}', camp.Status);
        htmlBody = htmlBody.replace('{!Campaign.Type}', camp.Type);
        system.debug('User Id -> '+ camp.CreatedById + 'Campaign Id -> '+ camp.Id);
        emails.add( mail );
        //Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    Messaging.sendEmail( emails );

   public void execute(SchedulableContext ctx){
        List<Campaign> campaign = new List<Campaign>();
        for(Campaign camp:campaign){
        system.debug('Campaign -> ' + camp.Name);}
        //EmailTemplate template = [select Subject, HtmlValue, Body from EmailTemplate where DeveloperName = 'Campaign_Due' Limit 1];
        EmailTemplate template = [select Subject, HtmlValue, Body from EmailTemplate where DeveloperName = 'CampaignDueToStart' Limit 1];
        if (template.Id != null){
        system.debug('Template Id -> ' + template.Id);
        sendMail(campaign, template);            
        system.debug('Template not found');

any inputs on this please?


2 Answers 2


I recommend you check this link. I've included an adaptation of that link's sample code

private class TestSchedulableClass {

   // CRON expression: midnight on March 15.
   // Because this is a test, job executes
   // immediately after Test.stopTest().
   public static String CRON_EXP = '0 0 0 15 3 ? 2022';

   static testmethod void test() {

      // Schedule the test job
      String jobId = System.schedule('Test my class',
                        new CampaignMailOnStartDate());

      // Get the information from the CronTrigger API object
      CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, 
         FROM CronTrigger WHERE id = :jobId];

      // Verify the expressions are the same

      // Verify the job has not run
      System.assertEquals(0, ct.TimesTriggered);

      // Verify the next time the job will run
      System.assertEquals('2022-03-15 00:00:00', 


  • Question : I'm checking the campaign details thru a query and based on that sending an email. how to handle this in the testcase along with the coverage of schedulable testclass? Commented Dec 10, 2015 at 16:40
  • After the test.stopTest() you can use asserts the way you would normally do in a class. The schedulable code will execute at that point. The link I provided will contain examples as well. Commented Dec 10, 2015 at 16:42
  • Thanks Sebastian, it works but only 60% coverage....I have a compaigns loop to send single messages...code coverage red for "Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); ...." Commented Dec 21, 2015 at 11:25
  • done now. thk u Commented Dec 21, 2015 at 12:54

I would recommend moving your code into more dedicated classes. Sending emails has nothing to do with Schedulable. Usually imho a Schedulable class should just look like:

public class MySchedulable implements Schedulable
    public void execute (SchedulableContext SC)

Then in your service class, you can break out the dedicated chunks that you need to test. Testing the Schedulable above is trivial, you just need to make sure a job is enqueued. Your other code contains a host of issues, among them:

  • You should never have an empty catch block (debug statements do not count as non-empty).
  • You have queries in a for loop. Move them out and use a Map.
  • You are replacing a hard coded set of merge fields, meaning if someone adds more to the template, your code will not find them. Either use regex matching or use the setWhatId method and take advantage of standard merge functionality. The latter will spare you the trouble of building out a dynamic query on Campaign.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .