7

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?

  • I believe there are other as well who are facing this issue. success.salesforce.com/issues_view?id=a1p300000008ZH4AAM – manjit5190 Dec 21 '15 at 19:47
  • Yeah, I shared a link to the exact same issue. – Programmable Medley Dec 21 '15 at 19:50
  • I missed the hyper-link you shared. Sorry about that. – manjit5190 Dec 21 '15 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. – Programmable Medley Dec 22 '15 at 1:49
  • 1
    @ProgrammableMedley That, good sir, is brilliant! You deserve many up votes. – Daniel Ballinger Dec 22 '15 at 18:34
8

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":"test@example.com","HtmlBody":"<html><body></body></html>","BccAddress":"test@example.com",'
        + '"CcAddress":"","ToAddress":"test@example.com","Subject":"Testing"},"contextId":"50011000005ZtcRAAS","actionType":"Email",'
        + '"actionName":"Case.Email","fromAddressList":["salesforce@test.com"]}]';
    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> { 'salesforce@test.com' }
  }
};

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.

| improve this answer | |
5

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

| improve this answer | |

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.