1

My schedulable apex is working properly and I have 100% test coverage on it, but the test is not giving me the System Assert I am expecting and I'm hoping someone can spot the reason why.

The Apex checks to see if any records (of the custom NVMStatsSF__NVM_Call_Summary__c object) have been made in the last 15 minutes, and then sends an email notification if there haven't been any.

It queries for records with a CallDate larger than 15 minutes ago (so within the last fifteen minutes) and returns a list of any records found. If there aren't any records found, it sends an email.

In my Test Class, I've created a list of records with a CallDate two hours old, but my System.Assert is telling me that no email is being sent. I see the list of records with IDs and old CallDates in my debug, but I can't seem to figure out why there's no email invocations.

This is my first time writing schedulable apex, sending an email from apex, and also writing test classes, so all tips are appreciated!

My Apex class:

//Watchdog Apex that checks every 15 minutes to make sure records are being created.

   public class NotifyVoiceCallCreation implements Schedulable {
       public void execute(SchedulableContext ctx) {
           CheckSummaries();
       }
       
    //Collect records that have a CallTime within the past fifteen minutes, limit 10.
   
        public void CheckSummaries(){        
           DateTime dt = System.Now().addMinutes(-15);
           List<NVMStatsSF__NVM_Call_Summary__c> summaries = [SELECT Id, NVMStatsSF__CallTime__c, NVMStatsSF__CLID__c
               FROM NVMStatsSF__NVM_Call_Summary__c
               WHERE NVMStatsSF__CallTime__c >= :dt
               ORDER BY NVMStatsSF__CallTime__c DESC
               LIMIT 10];
           //if there are no records returned in the query, send email notification
           if (summaries.isEmpty()){
               sendmail();
               System.debug('Warning- Summary records created equals ' + summaries.size());
           } else {
               System.debug('Summary records this past 15 minutes equals ' + summaries.size());
           }
       }
   
   public static void sendmail() {
       // Email message to be sent if no summary records are created within the last 15 minutes
       // Retrieves Id for donotreply and includes error handling
           OrgWideEmailAddress[] owea = [SELECT Id from OrgWideEmailAddress where Address = '[email protected]'];
           string [] toaddress= New string[]{'[my address inserted]'};
               
               Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
       if (owea.size() > 0) {
           email.setOrgWideEmailAddressId(owea.get(0).Id);
       }
               email.setReplyTo('[email protected]');
               email.setSubject('15 Minute Testing Apex Scheduler');
               email.setPlainTextBody('Warning: No Voice Call Summaries have been created in the last 15 minutes.');
               email.setToAddresses(toaddress);
               Messaging.sendEmail(New Messaging.SingleEmailMessage[]{email});
           }
       }

My Test Class:

    @IsTest(SeeAllData=true)
    public class NotifyVoiceCallCreationTest {
        //Dummy CRON expression: Because this is a test, job executes immediately after Test.stopTest()
        public static String CRON_EXP= '0 0 9-18 ? * 1-5';
        
        @IsTest
        static void testScheduledJob() {
            
    
            //Create old Summary records
            List<NVMStatsSF__NVM_Call_Summary__c> summaries = new List <NVMStatsSF__NVM_Call_Summary__c>();
                Datetime dt = System.Now().addHours(-2);
                for (Integer i=0; i<10; i++) {
                NVMStatsSF__NVM_Call_Summary__c s = new NVMStatsSF__NVM_Call_Summary__c(
                    NVMStatsSF__CallTime__c = dt
                );
                summaries.add(s);
            }
            insert summaries;
            
            Test.startTest();
            //Schedule the test job
            String jobId = System.schedule('ScheduledApexTest',
                    CRON_EXP,
                    new NotifyVoiceCallCreation());
            System.debug(summaries);
            //Verify the scheduled job has not run yet.
            Integer invocations = Limits.getEmailInvocations();
            System.assertEquals(0, invocations, 'Emails exist before job has run');
    
            Test.stopTest();
            invocations = Limits.getEmailInvocations();
            System.assertEquals(1, invocations, 'Email has not been sent');
    
      }
    }
4
  • Welcome to SFSE! Please take a moment to read How to Ask and take the tour. Including a Minimal, Complete and Reproducible Example would be helpful. Your example is minimal but it's not complete or reproducible.
    – identigral
    Aug 17, 2023 at 18:04
  • 1
    Thank you! I've updated the description and hopefully that gives enough context. This query is on a custom field on a custom object, but it can be easily replicated with CreatedDate on a standard object. Aug 17, 2023 at 18:57
  • 1
    Have you run the test class with debug logs enabled? If so, are you actually seeing the debugs statements you've got in the schedulable being fired at all, and if so are they the expected ones? The other thing that could have an impact is the use of SeeAllData=True. If there are other users in the org creating these records, you're going to see inconsistent results as the test isn't isolated and real records will be picked up by the scheduled apex. Aug 17, 2023 at 22:53
  • @CallumMacErlich Thanks for the comment! The debug statements are fired with expected values. The SeeAllData=True is to bypass a trigger in custom settings. IT is all system automated so there shouldn't be an issue there. Sep 14, 2023 at 23:26

1 Answer 1

1

Your issue is an iterpretation error on your side of the transaction limit. You have a transaction boundary for your scheduled apex and thus this means that your Limit.getEmailIvocation() is 0 in the test transaction.

Let me elaborate as in this example you actually have 3 transactions running :

  1. the test transaction itself - let's call it T1 - (you would have an additional one - T0 - if you had a testSetup)
  2. the startTest that starts Transaction 2 - let's call it T2 - that ends at stopTest
  3. the ASYNC transaction - let's call it T3

By the time you call your method invocations = Limits.getEmailInvocations(); your are back in T1, NOT in T3 hence the result of invocation is 0 and in that transaction, no email has been sent. The email is actually sent in T3, NOT in T2 NOR in T1.

EDIT

The order of transaction is as follow :

  1. Run the test -> T1
  2. at Test.startTest(); T2 starts
  3. at Test.stopTest(); T2 ends
  4. immediately after T2 ends, T3 starts and ends
  5. you are back in T1 -> and here you do your limit count which is indeed 0 email sent in transation T1
1
  • If T3 starts and ends immediately without an opportunity to run an invocation test, how would you suggest testing whether an email will be/has been sent? Sep 14, 2023 at 23:18

You must log in to answer this question.

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