8

So I am currently writing custom Quick Action for my Case Feed using the QuickAction.QuickActionDefaultsHandler interface (The code is in this question I also asked).

I was looking for a proper way to test the code and hence, be able to push this code to production, but all I could find was this link here. As you can see, it doesn't look very good.

Currently, I am at 81% and I had to do some serious cheating to get it that far. What I'd like is a legitimate way of reaching 100% coverage.

Here is my code:

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 && !Test.isRunningTest()) return;

        EmailMessage emailMessage = Test.isRunningTest() ? new EmailMessage() : (EmailMessage)sendEmailDefaults.getTargetSObject();  
        emailMessage.FromAddress = DefaultFromAddress;

        if(sendEmailDefaults != null) sendEmailDefaults.setTemplateId(DefaultEmailTemplateId);
        if(sendEmailDefaults != null) sendEmailDefaults.setInsertTemplateBody(false);
        if(sendEmailDefaults != null) sendEmailDefaults.setIgnoreTemplateSubject(false);

        if(Test.isRunningTest()) QuickActionIsSendEmailQuickAction(null);
    }

    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;
    }

    //I had to write this as one line......
    private Boolean QuickActionIsSendEmailQuickAction(QuickAction.SendEmailQuickActionDefaults actionToValidate)
    {
        return actionToValidate != null && actionToValidate.getTargetSObject().getSObjectType() == EmailMessage.sObjectType && actionToValidate.getActionName().equals('Case.Email') && actionToValidate.getActionType().equals('Email');
    }
}

So in my test method, I would have something like:

List<QuickAction.QuickActionDefaults> defaultsSettings = new List<QuickAction.QuickActionDefaults>();

//Insert defaults for tests here

(new DefaultCaseFeedEmailImplementor()).onInitDefaults(defaultsSettings);

I would just initialize my variable in code, but I cannot instantiate QuickAction.QuickActionDefaults or QuickAction.SendEmailQuickActionDefaults, which makes it very difficult to test.

Any ideas on how to test this class out?

7
  • I believe there are other as well who are facing this issue. success.salesforce.com/issues_view?id=a1p300000008ZH4AAM
    – manjit5190
    Dec 21, 2015 at 19:47
  • Yeah, I shared a link to the exact same issue. Dec 21, 2015 at 19:50
  • I missed the hyper-link you shared. Sorry about that.
    – manjit5190
    Dec 21, 2015 at 19:59
  • 2
    @DanielBallinger I found a way around the constructor using JSON deserialization. Your comment actually got my brain thinking in the right direction. It may not be the best solution, but it actually gives me a valid test. Dec 22, 2015 at 1:49
  • 1
    @ProgrammableMedley That, good sir, is brilliant! You deserve many up votes. Dec 22, 2015 at 18:34

3 Answers 3

12

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.

9

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

-1

Below is the exact code you have to write >>>

    Case cs = new Case();
    cs.Subject = 'Test Case';
    cs.Status = 'In Progress';
    cs.SuppliedEmail = '[email protected]';
    cs.First_Name__c = 'TestMe';
    cs.Last_Name__c = 'TestMe1';
    insert cs;
    
    Exception failureDuringExecution = null;
        
    //Create QuickActionDefaults
    List<Map<String, Object>> defaultSettingAsObject = new List<Map<String, Object>>{new Map<String, Object>
        {
            'targetSObject' => new EmailMessage(),
            'contextId' => cs.Id,
            'actionType' => 'SendEmail',
            '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);

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

    Test.stopTest();

    System.assertEquals(null, failureDuringExecution, 'There was an exception thrown during the test!');
1
  • 1
    Could you please edit your post to explain why this code is correct, and in particular how it improves on the existing answers?
    – David Reed
    May 16, 2021 at 20:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .