Test Class for QuickAction.QuickActionDefaultsHandler

After some thinking, I realized that I can create the instances I want by using JSON strings and deserializing them into my desireable mock implementations.

So, in my class implementing the QuickAction.QuickActionDefaultsHandler, I did the following:

...
private QuickAction.SendEmailQuickActionDefaults GetSendEmailQuickActionFromDefaultSettings(List<QuickAction.QuickActionDefaults> defaultsSettings) 
{
      System.Debug(LoggingLevel.Info, JSON.serailize(defaultsSettings));
.....

From there, I looked at the output of my Debug log to get an accurate JSON string representation I could use.

It took a while of trial and error to get everything to deserialize properly, but I finally go it down to a single test.

static testmethod void DefaultCaseFeedEmailImplementor_SimpleTest()
{
    //Create test data here
    Exception failureDuringExecution = null;

    String defaultsAsJSON = '[{"targetSObject":{"attributes":{"type":"EmailMessage"},"TextBody":"",'
        + '"FromName":"Test","FromAddress":"[email protected]","HtmlBody":"<html><body></body></html>","BccAddress":"[email protected]",'
        + '"CcAddress":"","ToAddress":"[email protected]","Subject":"Testing"},"contextId":"50011000005ZtcRAAS","actionType":"Email",'
        + '"actionName":"Case.Email","fromAddressList":["[email protected]"]}]';
    List<QuickAction.SendEmailQuickActionDefaults> defaultsSettings = 
        (List<QuickAction.SendEmailQuickActionDefaults>)JSON.deserialize(defaultsAsJSON, List<QuickAction.SendEmailQuickActionDefaults>.class);

    Test.startTest();
    try { (new DefaultCaseFeedEmailImplementor()).onInitDefaults(defaultsSettings); }
    catch(Exception failure) { failureDuringExecution = failure; }

    Test.stopTest();

    System.assertEquals(null, failureDuringExecution, 'There was an exception thrown during the test!');
    //Make other assertions here
}

Now that my code can properly go over a simple test scenario, I can change my code to the following:

public with sharing class DefaultCaseFeedEmailImplementor implements QuickAction.QuickActionDefaultsHandler
{
    private static final String DefaultEmailTemplateName = Label.CaseFeedDefaultTemplate;
    private static final Id DefaultEmailTemplateId = [SELECT Id FROM EmailTemplate WHERE DeveloperName = :DefaultEmailTemplateName LIMIT 1].Id;
    private static final String DefaultFromAddress = Label.CaseFeedDefaultFromAddress;

    public void onInitDefaults(List<QuickAction.QuickActionDefaults> defaultsSettings) 
    {
        QuickAction.SendEmailQuickActionDefaults sendEmailDefaults = GetSendEmailQuickActionFromDefaultSettings(defaultsSettings);
        if(sendEmailDefaults == null) return;

        EmailMessage emailMessage = (EmailMessage)sendEmailDefaults.getTargetSObject();  
        emailMessage.FromAddress = DefaultFromAddress;

        sendEmailDefaults.setTemplateId(DefaultEmailTemplateId);
        sendEmailDefaults.setInsertTemplateBody(false);
        sendEmailDefaults.setIgnoreTemplateSubject(false);
    }

    private QuickAction.SendEmailQuickActionDefaults GetSendEmailQuickActionFromDefaultSettings(List<QuickAction.QuickActionDefaults> defaultsSettings) 
    {   
        for(QuickAction.QuickActionDefaults defaultSetting : defaultsSettings)
        {
            if(!(defaultSetting instanceof QuickAction.SendEmailQuickActionDefaults)) continue;

            if(QuickActionIsSendEmailQuickAction((QuickAction.SendEmailQuickActionDefaults)defaultSetting))
                return (QuickAction.SendEmailQuickActionDefaults)defaultSetting;
        }

        return null;
    }

    private Boolean QuickActionIsSendEmailQuickAction(QuickAction.SendEmailQuickActionDefaults actionToValidate)
    {
        return actionToValidate.getTargetSObject().getSObjectType() == EmailMessage.sObjectType 
            && actionToValidate.getActionName().equals('Case.Email') 
            && actionToValidate.getActionType().equals('Email');
    }
}

Update 12/23/2015

I actually thought about it a bit more, and there is a way that is a bit easier to read.

Since I know serializedUntyped returns a Map<String, Object>, I can change a Map<String, Object> into practically anything. So for this example I can do the following in my test class:

List<Map<String, Object>> defaultSettingAsUntypedObject = new List<Map<String, Object>>
{
  new Map<String, Object>
  {
        'targetSObject' => new EmailMessage(),
        'contextId' => '50011000005ZtcRAAS',
        'actionType' => 'Email',
        'actionName' => 'Case.Email',
        'fromAddressList' => new List<String> { '[email protected]' }
  }
};

List<QuickAction.SendEmailQuickActionDefaults> defaultsSettings = 
    (List<QuickAction.SendEmailQuickActionDefaults>)JSON.deserialize(JSON.serialize(defaultSettingAsObject), List<QuickAction.SendEmailQuickActionDefaults>.class);

It works the same way and I'm not having to guess if I messed up my JSON string or not. It is a little bit longer but much easier to read and add extra values where I need them.


In Summer'16 release Salesforce added new method which helps with testing classes implementing QuickAction.QuickActionDefaultsHandler interface:

Test.newSendEmailQuickActionDefaults(contextId, replyToId)

So you can create records without JSON. Example:

    QuickAction.SendEmailQuickActionDefaults sendEmailDefaults = 
Test.newSendEmailQuickActionDefaults(case.Id, null);
    Test.startTest();
        CaseEmailTemplateSelector cntl = new CaseEmailTemplateSelector();
        cntl.onInitDefaults(defaults);
    Test.stopTest();
    EmailMessage emailMessage = (EmailMessage) sendEmailDefaults.getTargetSObject();
    System.assertNotEquals(null, emailMessage);

Please see Test Class Methods article in developer guide: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_test.htm