0

I have a scenario in which an email is being sent to Users having admin profile at the end of the month for Closed Opportunities.

Code seems to be working fine when tested in execute anonymous window but in the test class it seems like scheduler is not working properly. I have queried the CronTrigger info after Test.stopTest() and asserting the TimesTriggered which I think should be 1 but it is still showing its value as 0. Email invocation is also not working (though I have commented this code here). "invocations" value in the assertion statement is 0 when it should be 1.

Not sure what I am doing wrong here.

Below are the classes:

// Send email to users having System Administrator profile about the closed opportunities.
public class EmailNotificationScheduler implements Schedulable {
    public void execute(SchedulableContext sc) {
        // Get list of opportunities which were closed won/lost in the month
        List<Opportunity> listOfClosedOps = [Select Id, Name, StageName 
                                             From Opportunity 
                                             Where CloseDate = THIS_MONTH and (StageName='Closed Won' or StageName='Closed Lost')];
        
        // Get user ids in a list
        Profile adminProfile = [Select Id From Profile Where Name = 'System Administrator'];
        List<Id> listOfUserIds = new List<Id>();
        for(User usr : [Select Id From User Where ProfileId = :adminProfile.Id]) {
            listOfUserIds.add(usr.Id);
        }
        
        // Get all the opportunities Name and StageName in a String
        String closedOpportunities = 'Opportunity Name' + ' : ' + 'Status';
        for(Opportunity op : listOfClosedOps) {
            closedOpportunities = ClosedOpportunities + '\n' + op.Name + ' : ' + op.StageName;
        }
        
        sendmail(listOfUserIds, closedOpportunities);
    }
    
    public void sendmail(List<Id> listOfUserIds, String closedOpportunities) {
        // Creating instance of email and set values
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setToAddresses(listOfUserIds);
        email.setSubject('Opportunities closed this month');
        email.setPlainTextBody(closedOpportunities);
        
        // Send email
        Messaging.sendEmail(new List<Messaging.SingleEmailMessage> {email});
    }
}
@isTest
private class EmailNotificationSchedulerTest {
    @testSetup
    static void dataSetup() {
        Opportunity op1 = new Opportunity(Name = 'ClosedWonOpportunity', StageName = 'Closed Won', CloseDate = System.today());
        insert op1;
        
        Opportunity op2 = new Opportunity(Name = 'ClosedLostOpportunity', StageName = 'Closed Lost', CloseDate = System.today());
        insert op2;
    }
    
    @isTest
    static void testEmailNotificationScheduler() {
        // Schedule the job inside the start and stop test methods
        Test.startTest();
            String cronExp = '0 0 18 LW * ?';
            String jobId = System.schedule('Closed Opportunities for the Month', 
                                           cronExp, 
                                           new EmailNotificationScheduler());
            //Integer invocations = Limits.getEmailInvocations();
        Test.stopTest();
        
        System.assertEquals(false, String.isBlank(jobId), 'JobId should not be empty');
        
        // Get the information from CronTrigger object
        CronTrigger cronInfo = [Select Id, CronExpression, TimesTriggered, NextFireTime From CronTrigger Where Id = :jobId];
        //System.debug(cronInfo);
        System.assertEquals(1, cronInfo.TimesTriggered, 'CronTrigger did not fire');
        //System.assertEquals(1, invocations, 'Email was not sent');
    }
}
2
  • 2
    It's pointless to test Schedulable code by worrying about CronTriggers. Just do SchedulableContext sc; new EmailNotificationScheduler().execute(sc); Why? Because SFDC guarantees that schedulable classes will execute on or around the CronTrigger time. Less is more
    – cropredy
    Dec 24, 2020 at 18:02
  • 2
    Taking Cropredy's comment further - you don't need to test that System.schedule works (that is a given) just that your schedulable implementation does.
    – Phil W
    Dec 24, 2020 at 20:54

1 Answer 1

1

Test.stopTest() executes queued anonymous code, then resets the governor limits afterwards. You'll never see things like Limits.getEmailInvocations greater than zero, or other consumed limits. Further, things like "LastRunTime" aren't run until the transaction finalizes, which for a unit test, is after the last line of code executes. Accordingly, you can't rely on this value being correct during your unit test.

The same thing happens with other post-transaction finalizations, like Field History; these records simply are not possible to create during a unit test. You can set a static Boolean variable and check if that flag is set, but this is probably overkill. Just allow the code to run and move on to the next problem. You don't need to test system functionality, much like writing a test for 1+1 would be a fruitless endeavor--if the laws of math somehow change, we have bigger problems to worry about than a unit test.

1
  • I have seen in other threads that CronTrigger.TimesTriggered should work in case of schedulers in Unit Test. I was just wondering what could be wrong here. But I understand that it is irrelevant to test system's functionality and would update my code accordingly. Thanks for all the help. :) Dec 25, 2020 at 5:57

You must log in to answer this question.

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