1

I'm trying to write a test class to cover the try/catch section in the following code but am unsure how to test for the error. I only have 69% code coverage w/o it.

Or is there a way in the test class to force the EmailFailure string to not be null so I can get coverage on that section?

trigger Events_SummerCamp_AutoEmail on CnP_PaaS_EVT__Event_registrant_session__c (before insert, before update) {

List<Messaging.SingleEmailMessage> emails = new List<Messaging.SingleEmailMessage>();

List<ID> RequireContactId = new List<ID>();
List<ID> RequireEventId = new List<ID>();

for(CnP_PaaS_EVT__Event_registrant_session__c  CP : trigger.new) 
{
    if(CP.CnP_PaaS_EVT__ContactId__c != null && CP.CnP_PaaS_EVT__EventId__c != null){ 
        RequireContactId.add(CP.CnP_PaaS_EVT__ContactId__c);
        RequireEventId.add(CP.CnP_PaaS_EVT__EventId__c);
    }    
}

Map<ID,Contact> ContactData = new Map<Id,Contact>([Select Owner.Email, OwnerId, Email, Name, ID from Contact where id in : RequireContactId limit 1000 ]);    
Map<ID,CnP_PaaS_EVT__Event__c> EventData = new Map<Id,CnP_PaaS_EVT__Event__c>([Select ID, Name from CnP_PaaS_EVT__Event__c where ID in : RequireEventId limit 1000 ]);    

for(CnP_PaaS_EVT__Event_registrant_session__c  newCP: trigger.new){
    if (newCP.CnP_PaaS_EVT__ContactId__c != NULL && newCP.CnP_PaaS_EVT__EventId__c != NULL && newCP.Email_Sent__c == FALSE){
        Contact theContact = ContactData.get(newCP.CnP_PaaS_EVT__ContactId__c); 
        CnP_PaaS_EVT__Event__c theEvent = EventData.get(newCP.CnP_PaaS_EVT__EventId__c);   
        String EventName = theEvent.Name;
        if (EventName.Contains('Summer Camp')== TRUE){

            if (theContact.Email != NULL){
                User uEmail = [select Email from User where Id = :newCP.OwnerID limit 1]; 
                OrgWideEmailAddress[] owea = [select Id from OrgWideEmailAddress where Address = :uEmail.Email limit 1];
                Messaging.SingleEmailMessage sendEmail = new Messaging.SingleEmailMessage();

                //Summer Camp Email
                sendEmail.setTemplateID('00Xd0000001EkoC');

                sendEmail.setTargetObjectId(theContact.Id);
                sendEmail.setWhatId(newCP.id);

                if ( owea.size() > 0 ) {
                    sendEmail.setOrgWideEmailAddressId(owea.get(0).Id);
                }else{
                    sendEmail.setOrgWideEmailAddressId('0D2d0000000CbZv');                  
                }

                emails.add(sendEmail);

                newCP.Email_Sent__c = TRUE;
            } 
        }

    }

}   

String EmailFailure = null;
try {
    Messaging.SendEmailResult[] mailResult = Messaging.sendEmail(emails);
    if ( !mailResult[0].isSuccess() ) {
        EmailFailure = 'Email Failed: ' + mailResult[0].getErrors()[0].getMessage();
    }
}
catch(DmlException dmlEx) {
    EmailFailure = 'Email Failed: ' + dmlEx;
}
catch(System.EmailException emlEx) {
    EmailFailure = 'Email Failed: ' + emlEx;
}

if ( EmailFailure != null ) {

    Messaging.SingleEmailMessage mail=new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[] {'myemail@gmail.com'};
    mail.setToAddresses(toAddresses);
    mail.setReplyTo('myemail@gmail.com');
    mail.setSenderDisplayName('Apex error message');
    mail.setSubject('Summer Camp Error');
    mail.setPlainTextBody(EmailFailure);
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

}   

}

Test Class:

@isTest
public class Events_SummerCamp_Email_Test{
static testmethod void test_SC(){

        Contact c = new Contact(
            LastName = 'Testclass', 
            RecordTypeId = '012d0000000syet',
            Email = 'test@emails.com'
        );
        insert c;

        CnP_PaaS_EVT__Event__c e1 = new CnP_PaaS_EVT__Event__c(
            Name = '2014 Summer Camp',
            CnP_PaaS_EVT__Event_title__c = 'Summer Camp 2014',
            CnP_PaaS_EVT__Site_Url__c = 'https://ecad1.secure.force.com/Events',
            CnP_PaaS_EVT__Start_date_and_time__c = system.today(),
            CnP_PaaS_EVT__End_date_and_time__c = system.today(),
            CnP_PaaS_EVT__Publish_date__c = system.today()  
        );
        insert e1;

        CnP_PaaS_EVT__Event__c e = [select id from CnP_PaaS_EVT__Event__c where Name LIKE '%Summer Camp%' limit 1 ];

        CnP_PaaS_EVT__Event_registrant_session__c r = new CnP_PaaS_EVT__Event_registrant_session__c(
            Name = 'Testclass',
            CnP_PaaS_EVT__ContactId__c = c.Id,
            CnP_PaaS_EVT__EventId__c = e.Id              
        );
        insert r;

        CnP_PaaS_EVT__Event_registrant_session__c r2 =  [select id,Email_Sent__c from CnP_PaaS_EVT__Event_registrant_session__c where id = :r.id limit 1 ];

        System.assertEquals(TRUE, r2.Email_Sent__c);
}  
}
  • 1
    DMLException can't happen from sendEmail; that said, testing email send in sandboxes is problematic because some sandboxes will have email deliverability turned off so any attempt to send apex email will throw an exception – cropredy Mar 10 '17 at 18:00
  • 1
    To test exceptions you can add a try/catch in your test class as well that executes the code that will throw the exception, and then use asserts to check if the exception caught in the test class was what you expected to be thrown. Otherwise, any exception thrown will cause that testmethod to fail – Brian Mansfield Mar 10 '17 at 18:05
  • 1
    Sending an email when you encounter EmailException isn't the most logical approach...what if the exception was that you've exhausted your daily limit? Also worth noting that sending emails to a User using TargetObjectId does not consume that daily limit, whereas any usage of toAddresses does. – Adrian Larson Mar 10 '17 at 18:08
  • @BrianMansfield How do I execute the code that will throw the exception in the test class? I'm not sure how to force an email failure or what that code looks like. Our problem is that when an email bounces, it causes an error on this trigger, which then prevents a bunch of other things happening with our donations app Click & Pledge. I wanted to catch the error of an email bouncing so that the rest of the processes will continue. – Jennifer Mar 10 '17 at 18:32
  • email bounce can't be caught here - the email bounce event occurs long after the SFDC/Apex transaction has ended – cropredy Mar 10 '17 at 18:54
3

As per my experience to cover the Email exception block you need to throw an exception explicitly if its in a Test context, sample below:

if(Test.isRunningTest()) {
    throw new EmailException('Email Exception occurred for testing'); 
}

This will get caught in the EmailException catch and will cover that block.

  • Definitely not so for most DML exceptions. – Adrian Larson Mar 10 '17 at 19:20
  • Yes thats true, most of the DML exceptions can be covered, I have updated my answer. – Lakshman Mar 10 '17 at 19:28
  • 1
    I would add a @TestVisible static Boolean flag to control this behavior as well, since you probably want to also run tests where the functionality succeeds. – Adrian Larson Mar 10 '17 at 19:38
  • Today I learned that exception subtypes can be constructed and thrown. I always thought that all the standard exception classes were like System.Exception, which can't be constructed. Was also going to suggest a controlling variable, but Adrian beat me to that one. – Derek F Mar 10 '17 at 19:40
  • 1
    Add it after this line in your trigger- Messaging.SendEmailResult[] mailResult = Messaging.sendEmail(emails); – Lakshman Mar 15 '17 at 3:01
1

I find that it is easier to cover your exception handling using separation of concerns. Create a method dedicated to error handling, and this method will be easy to test in isolation, without worry about actually coercing the source exception to be thrown:

public static void handleEmailException(EmailException e)
{
    // handling logic here
}

Then your parent method would only be missing one line of coverage if you can't get the following to throw:

try
{
    // do stuff
}
catch (EmailException e)
{
    handleEmailException(e);
}

And your tests will already verify that the exception is properly handled, so this one missing line would be acceptable in most cases (I would far prefer to leave it uncovered than add conditional error throwing logic).

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.