1

If I send an email via Apex

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setTargetObjectId(someContactId);
mail.setTemplateId(someTemplateId);
mail.setSaveAsActivity(true);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

How can I create a unit test and test whether it was sent? Checking e.g. Limits.getEmailInvocations() doesn't actually work, because the emails are not sent out for real during tests. Or should I simpy assume that it works for the given contact and emailtemplate?

  • 1
    FYI - I have several tests that use limits.getEmailInvocations and they pass successfully and seem to be incremented during tests. SO if you need to test an email was sent like during an error condition that is caught, you can use this to test it was sent – Eric Oct 27 '17 at 2:23
3

Testing emails involves many things

  1. To test that the properties of an email are properly set and that your code actually tries to send an email, see this Stackexchange answer that exploits ApexMocks.
  2. To test that an email template is properly rendered before sending it, the savepoint - rollback hack is no longer required. You can use built-in Apex methods that render an email template. These are described in this stackexchange answer

The latter use case will mean you should create a wrapper method around your rendering of the template so you can unit test that method in a testmethod by passing in different arguments and verifying the proper application of merge fields to the template

For example

public Messaging.SingleEmailMessage render(ID templateId, ID whoId, ID whatId) {
 return Messaging.renderStoredEmailTemplate(templateId, whoId, whatId);
}

and the testmethod

// Given Sobjects corresponding to the whoId and whatId. Mock or fetch an email template and note the Id

Account a = new Account(...);
insert a;
Contact c = new Contact(...);
insert c;
EmailTemplate et;

System.runAs([select Id from User where Id = :Userinfo.getUserId() limit 1][0]) {       // creating setup objects requires diff context
  Folder[] folders = [select Id from Folder 
                        where AccessType IN: new list<String>{'Shared','Public'} 
                          AND Type = 'Email'];
  System.assert(!folders.isEmpty(),'test can\'t run wo Email Template folders. Folders can\'t be mocked in Apex');

  //    Given an EmailTemplate with merge fields
  et = new EmailTemplate(   Name = 'TestEmailTemplate ApexTestMethod',
                            Body = someBodyWithMergeFields, 
                            DeveloperName = 'TestEmailTemplate_ApexTestMethod', 
                            FolderId = folders[0].Id,
                            HtmlValue =someBodyWithMergeFields,
                            IsActive = true,
                            TemplateType = 'custom');
  insert et;            
} 

// When rendering...
Messaging.SingleEmailMessage res = new myClass().render(et.Id, c.id, a.Id);

// Then verify rendered template

System.assertEquals(someExpectedRenderedTemplate,res.getHtmlBody());

Note that you can't use mocked SObjects in memory here to test the above because the code under test actually queries the database given the Ids passed.

| improve this answer | |
1

As Messaging.sendEmail() is Salesforce build in functionality, there's no need to test it. The only thing you can test is setting email up. I guess you should be able to attempt to send an email, then rollback it like:

Savepoint sp = Database.setSavepoint();
Messaging.sendEmail(new Messaging.SingleEmailMessage[] {tempMail});
Database.rollback(sp);

and verify if the tempMail is filled with the correct data.

| improve this answer | |
  • I'm only not sure if you can use savepoint and rollback in test methods, but I can't see any reason why it shouldn't. Please let me know if it work as it should. – Bart Juriewicz Oct 15 '15 at 8:55
  • We want to test the email templates to see if they merge correctly and throw no errors for several contacts. I'll try and see if this works and check the HTMLbody of mail for content. I'm thinking I already tried that and it didn't work, but let me check again :-) – Willem Mulder Oct 15 '15 at 8:58
  • It is important to ask yourself if it is possible that the code would stop working because of some changes in the future. If so, then that's a good idea to create unit test for this. If no, then I would make some initial manual testing. – Bart Juriewicz Oct 15 '15 at 9:08
  • 1
    @willemMulder - The rollback technique is no longer required. See Apex methods renderEmailTemplate and renderStoredEmailTemplate: developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… – cropredy Oct 24 '17 at 22:17
  • @cropredy correct! I'm already using that in many projects. If you want to add it as an answer, I'll gladly accept that one. – Willem Mulder Oct 26 '17 at 8:29

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.