19

I have a Batch class that sends an Email from its finish method and want to test that. a But....I cannot test it. The the above test fails as there was no email invocation at all. BUT: I see it in the Limits view of my Dev Console that indeed one email invocation happens. Why?!

The batch class:

public MyBatch implements Database.Batchable<SObject> {

    ...

    public void finish(Database.BatchableContext context) {
        sendEmail();
    }
}

The test method:

@isTest
private static void sendsEmailOnFinish() { 
    Test.startTest();   
    MyBatch b = new MyBatch();
    Database.executeBatch(b);
    Test.stopTest();


    // Verify
    System.assertEquals(1, Limits.getEmailInvocations());
}

I thought this might be connected to using Test.startTest() and stopTest() but even moving the assertEquals before the Test.stopTest() doesn't make my test run.

10

Batches don't run until stopTest(), and stopTest() restores the test method's limits after the batch executes before it returns. This means that your test method will never see that limit set unless it sends an email itself (which, of course, will never be delivered/sent).

You have two ways to handle this:

  1. Don't bother with the assert. Assuming the email isn't sent, an exception is thrown; if you don't catch the exception in finish, the exception will be thrown out to the test method, and if you don't have a try-catch block, the entire test automatically fails anyways, which is the point of your assert call, right?

  2. Set a static @TestVisible method in your MyBatch class. On the last line of finish, set that value to Limits.getEmailInvocations(). Once you come back from the batch, you can examine this value.


global class MyBatch ... {
    @TestVisible static Integer emailLimits;
    global void finish(...) {
        ...
        MyBatch.emailLimits = Limits.getEmailInvocations();
   }
}
| improve this answer | |
  • I couldn't get this to work correctly. Limit.getEmailInvocations() returns 0 in the finish method. – TemporaryFix Mar 5 '18 at 20:45
  • @Programatic Presumably, then, no emails were sent. You would need to share your code. Please consider asking a new question with your code, any errors you get, and a brief description. We'll be glad to help you out. – sfdcfox Mar 5 '18 at 20:48
  • I've figured it out. I was sending the emails in my execute method, but was setting the limit in my finish method. Setting it in the execute method worked – TemporaryFix Mar 5 '18 at 21:11
  • @Programatic Ah, good. I'm happy you were able to figure it out. – sfdcfox Mar 5 '18 at 21:17
2

Just a bit lengthy process, but you will be able to test that. At the start of the test create the custom setting with any value and name.

Now need to create additional class say EmaiHandler class which implement the Messaging.InboundEmailHandler interface.

global class EmailServiceHandler implements Messaging.InboundEmailHandler 
{
        global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,Messaging.InboundEnvelope envelope) 
        {
    if (email.plainTextBody== 'Sample Email')
           {
              // some logic to update the custom setting u have created
           } 
           Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
           return result;
 }
}

Now u need to configure your email setting in the org and need to check out the email id that the salesforce org will listen.Refer to below link to do the same.

http://wiki.developerforce.com/page/An_Introduction_To_Email_Services_on_Force.com

Now after wards in the test class at the start of the test initialize the custom setting with some dummy value.

before calling the batch pass the emailid(eg. myhandler@x4m9vxsizy32oug74yh6rhj7e.in.salesforce.com) that the salesforce will listen and the body string that u have mentioned in the handler class.

Now execute the batch .

if(customSetting__c.value =='Set in Handler Class') { It means the email is transmitted; put assert statements; }

This completes your testing for the same.

| improve this answer | |
  • I thought of that as well but thats way to complex. I do not care if such a email is really sent or delivered. I would be totally happy if I understood why the Limits methods is not showing the sent email after calling Test.stopTest()... – Robert Sösemann Aug 27 '13 at 21:13
  • 1
    This email handler bit won't work. The email isn't actually sent. – sfdcfox Aug 27 '13 at 21:35
  • Well as per my understanding on salesforce since the batch is an asynchronous call, so the email that is sent will be done in the transaction that is related to batch. Now that transaction got over once you are out of batch. So limits method will start giving the result as 0. So i think if u want to perform the same thing then better create a custom setting, set it true or false in the batch after the email is sent. and retrieve the same in the test again to ensure if the mail is sent or not. – Mukul Goel Aug 27 '13 at 21:37
  • @sfdcfox:- The email handler will work because the things in test class will happen but will not get committed. Also the email is not sent to any external address It is with in the organization,So i dont see any potential issues in that. – Mukul Goel Aug 27 '13 at 21:42
  • sendEmail spools the email. It's not actually sent, and since there's no commit, it never goes out. Have you actually tested this mechanism? It's not documented anywhere, and I wouldn't rely on that working... AND, there's a much easier way than to waste a custom setting on this. – sfdcfox Aug 27 '13 at 21:47
1

We had a similar problem and in our case we solve it in the following way, we just moved Limits.getEmailInvocations() method inside of Test.startTest()\stopTest().

@isTest
private static void sendsEmailOnFinish() { 
    Test.startTest();   
    MyBatch b = new MyBatch();
        Database.executeBatch(b);

        // Verify
        System.assertEquals(1, Limits.getEmailInvocations());
    Test.stopTest();



}

Salesforce has a different set of limits of what is done inside of Test.startTest()\stopTest() methods and outside of them. This is why your original version doesn't work.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.