0

Normally, I expect tests to fail if the org does not allow email deliverability. In my current sandbox, I have several tests that fail because email is not deliverable. I think most of them fail at Messaging.reserveSingleEmailCapacity(emailCount); but I could be mistaken.

But I also have some tests which pass, despite actually trying to send an email like Messaging.sendEmail(emailMessageList);

... But this isn't necessarily "safe" as I have two different tests, one of which passes that point and the other fails.

For example, I have the following wrapper class for sending Emails so I can mock out the results when testing other classes:

// EmailMailer wrapper class:
public with sharing virtual class XAP_EML_EmailMailer {
    private XAP_DAO_ApexEmailNotificationSelector apexEmailNotificationSelector;

    @TestVisible
    private XAP_EML_EmailMailer(XAP_DAO_ApexEmailNotificationSelector apexEmailNotificationSelector) {
        this.apexEmailNotificationSelector = apexEmailNotificationSelector;
    }

    protected XAP_EML_EmailMailer() {
        this(XAP_DAO_ApexEmailNotificationSelector.getInstance());
    }

    private static XAP_EML_EmailMailer instance;
    public static XAP_EML_EmailMailer getInstance() {
        if (instance == null) {
            instance = new XAP_EML_EmailMailer();
        }
        return instance;
    }

    // This enables workaround to prove emails would be sent even in orgs where email is disabled.
    @TestVisible
    private static Integer MESSAGES_SENT_COUNT = 0;

    public virtual XAP_EML_SendEmailResultListWrapper sendExceptionAlerts(XAP_EML_EmailAlertable alertable) {
        List<User> userList = new List<User>();
        List<String> emailAddressList = new List<String>();
        for (ApexEmailNotification apexEmailNotification : this.apexEmailNotificationSelector.selectAll()) {
            if (apexEmailNotification.User != null) {
                userList.add(apexEmailNotification.User);
            }
            else {
                emailAddressList.add(apexEmailNotification.Email);
            }
        }

        List<Messaging.SingleEmailMessage> emailMessageList
                = this.buildEmailMessageList(userList, emailAddressList, alertable);
        return this.send(emailMessageList);
    }

    @TestVisible
    private List<Messaging.SingleEmailMessage> buildEmailMessageList(
            List<User> userList,
            List<String> emailAddressList,
            XAP_EML_EmailAlertable alertable
    ) {
        List<Messaging.SingleEmailMessage> emailMessageList = new List<Messaging.SingleEmailMessage>();
        emailMessageList.addAll(this.buildEmailMessageList(userList, alertable));
        emailMessageList.addAll(this.buildEmailMessageList(emailAddressList, alertable));
        return emailMessageList;
    }

    @TestVisible
    private List<Messaging.SingleEmailMessage> buildEmailMessageList(
            List<User> userList,
            XAP_EML_EmailAlertable alertable
    ) {
        List<Messaging.SingleEmailMessage> emailMessageList = new List<Messaging.SingleEmailMessage>();
        for (User user : userList) {
            Messaging.SingleEmailMessage email = this.createEmail(alertable);
            email.setTargetObjectId(user.Id);
            emailMessageList.add(email);
        }
        return emailMessageList;
    }

    @TestVisible
    private List<Messaging.SingleEmailMessage> buildEmailMessageList(
            List<String> emailAddressList,
            XAP_EML_EmailAlertable alertable
    ) {
        List<Messaging.SingleEmailMessage> emailMessageList = new List<Messaging.SingleEmailMessage>();
        if (!emailAddressList.isEmpty()) {
            Messaging.SingleEmailMessage email = this.createEmail(alertable);
            email.setToAddresses(emailAddressList);
            emailMessageList.add(email);
        }
        return emailMessageList;
    }

    @TestVisible
    private Messaging.SingleEmailMessage createEmail(XAP_EML_EmailAlertable alertable) {
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setSaveAsActivity(false);
        email.setSubject(alertable.getEmailSubject());
        email.setPlainTextBody(alertable.getEmailBody());
        return email;
    }

    public virtual XAP_EML_SendEmailResultListWrapper send(List<Messaging.SingleEmailMessage> emailMessageList) {
        MESSAGES_SENT_COUNT++;
        return new XAP_EML_SendEmailResultListWrapper(
                Messaging.sendEmail(emailMessageList)
        );
    }
}

But, of course, I need and want to create tests for this class too.

The following test directly executes send() which executes Messaging.sendEmail(emailMessageList) and this passes.

@IsTest
public static void testSendShouldSignifySentMessagesByAugmentingCounter() {
    // Arrange
    Messaging.SingleEmailMessage testMessage = new Messaging.SingleEmailMessage();
    testMessage.setSaveAsActivity(false);
    testMessage.setTargetObjectId(TEST_RUNNER.Id);
    testMessage.setPlainTextBody(TEST_BODY);
    List<Messaging.SingleEmailMessage> testMessageList = new List<Messaging.SingleEmailMessage>{
            testMessage
    };

    // Act
    XAP_EML_SendEmailResultListWrapper result;
    Test.startTest();
    System.runAs(TEST_RUNNER) {
        result = MAILER_UNDER_TEST.send(testMessageList);
    }
    Test.stopTest();

    // Assert
    System.assertEquals(1, XAP_EML_EmailMailer.MESSAGES_SENT_COUNT);
    System.assert(result.sendEmailResults[0].isSuccess());
}

However, this test only indirectly executes send() which executes Messaging.sendEmail(emailMessageList) but this fails.

public static void testSendExceptionAlertsShouldSendAlertsToBothUsersAndArbitraryEmails() {
    // Arrange
    MOCK_APEX_EMAIL_NOTIFICATION_SELECTOR.returnedApexEmailNotificationList = TEST_APEX_EMAIL_NOTIFICATION_LIST;

    TEST_ALERTABLE.returnedEmailSubject = TEST_SUBJECT;
    TEST_ALERTABLE.returnedEmailBody = TEST_BODY;

    // Act
    XAP_EML_SendEmailResultListWrapper result;
    Test.startTest();
    System.runAs(TEST_RUNNER) {
        result = MAILER_UNDER_TEST.sendExceptionAlerts(TEST_ALERTABLE);
    }
    Test.stopTest();

    // Assert
    System.assertEquals(1, XAP_EML_EmailMailer.MESSAGES_SENT_COUNT);
    System.assert(result.sendEmailResults[0].isSuccess());
    System.assert(result.sendEmailResults[1].isSuccess());
}

Why does the second test get a different result?

Which messaging operations are safe for testing in orgs where email deliverability has been turned off (and therefore don't need to be avoided while trying to make tests which pass in all orgs)?

3
  • 2
    It seems like you have answered your own question. I think the sendEmail call itself passes either way, but reserveSingleEmailCapacity errors out? – Adrian Larson Aug 21 '20 at 16:30
  • Is that something I can rely on? Is there some documentation which itemizes which of Messaging's methods will throw errors? (I would not have expected that sending emails would fail silently when merely reserving capacity makes the system blow up....) – Brian Kessler Aug 21 '20 at 20:48
  • As noted in the revisions above, this difference is clearly something that I can't rely on since I have two tests, one of which allows sendEmail to pass and the other does not, and I'm not clear why the result is different. – Brian Kessler Aug 24 '20 at 8:11

Your Answer

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

Browse other questions tagged or ask your own question.