2

I'm working on a trigger that will email users when a specific field is changed. I am having trouble creating a test class that will give me proper code coverage. I am new to this kind of thing so help creating a test class would be appreciated:

trigger onChange on Account (after update) {
    for (Account acc: Trigger.new) {
        Account oldAccount = Trigger.oldMap.get(acc.ID);
        if (acc.Group_ID__c != oldAccount.Group_ID__c) {
            Messaging.reserveSingleEmailCapacity(2);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {'email@email1.com','email@email2.com','email@email3.com'};        
            mail.setToAddresses(toAddresses);
            mail.setSenderDisplayName('Salesforce Support');
            mail.setSubject('Group ID Changed: '+ acc.Name);
            mail.setPlainTextBody('The Group ID of '+acc.Name+' has changed from: \n'+ oldAccount.Group_ID__c + '\n to \n' + acc.Group_ID__c);
            Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
        }
    }
}

Here's what I got for my test so far:

public class testDataOnChange {

   public static Account createAccount(Boolean item){
       Account acc = Account();

    }
    public static Account createAccount(String thename){
        Account acc = new Account(
            Name = thename,
            Group_ID__c = 'as344'
        );
        insert acc;
        return acc;
    }
}
2
  • 3
    Why not use a workflow rule for this?
    – martin
    Jan 29, 2016 at 17:07
  • I don't think i can because the I need the email to be sent only if the field is changed. The email also needs to contain the old field data as well as the new field data
    – Andy Scott
    Jan 29, 2016 at 17:17

2 Answers 2

3

You have a particularily difficult example to test for a beginner.

First, the usual clarification:

  • Tests are not to provide test coverage, but to verify that your business logic is correct.

Having said that, I can give you some tips about your tests:

  • Your test class and methods need to be annotated with @isTest to be ran as tests
  • Your test needs to actually exercise your trigger. In a few words, your trigger runs on account update, and if the group ID has changed, send an email. So, your test class needs to update an account and change the group ID so that the trigger fires, then assert that the email has been sent.
  • You can cover more cases: for instance, update the account without changing the group ID, and check that the email is not sent.
  • I found a clever way to verify that the email has been sent, in this question: How to test if outbound email is send

So, your test class could look like this:

@isTest
public class testDataOnChange {

    @isTest
    public static void emailIsSentIfGroupIdChanges ()
    {
        Account acct = new Account (Name = 'Test', Group_ID__c = 'Original');
        insert acct;          

        System.startTest ();
        acct.GroupId__c = 'Modified';
        update acct;  // This should fire your trigger and send email
        System.endTest ();

        System.AssertEquals (1, Limits.getEmailInvocations());
    }

    @isTest
    public static void emailIsNotSentIfGroupIdDoesNotChange ()
    {
        Account acct = new Account (Name = 'Test', Group_ID__c = 'Original');
        insert acct;          

        System.startTest ();
        acct.Name = 'Modified';
        update acct;  // This should fire your trigger but not send email
        System.endTest ();

        System.AssertEquals (0, Limits.getEmailInvocations());
    }
}

Disclaimer: I haven't actually ran this test. It's just an idea on how you could do it.

Edit: I have also added the case for when your group Id does not change and your email is not sent.

There's some code repetition in there. You could easily extract common code paths to a common method, but I think this way the test is clearly illustrated.

3
  • If you want to assert on the contents of your email, then you have to extract the email sending code to a class, create an interface for it, stub it out for tests, and make assertions about the contents, which is a bit more complicated.
    – mkorman
    Jan 29, 2016 at 17:40
  • 1
    This worked perfectly, and good to know about test coverage. This has provided me with a beginners insight into building and rolling out apex triggers to production
    – Andy Scott
    Feb 4, 2016 at 21:29
  • Glad I could help :)
    – mkorman
    Feb 5, 2016 at 10:24
0

It is possible to do this with workflow rules. Detecting whether a field value has been changed is easy enough to do with the formula editor's ISCHANGED(field) function. Getting old field values isn't something that an email template would be able to do directly, but you could add another custom field to hold that value and update it with another workflow rule after the email is sent.

./package.xml

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Account.Group_ID_Updated__c</members>
 <!--   <members>Account.Group_ID__c</members>   -->
        <members>Account.Old_Group_ID__c</members>
        <name>CustomField</name>
    </types>
    <types>
        <members>unfiled$public/AccountGroupIdChanged</members>
        <name>EmailTemplate</name>
    </types>
    <types>
        <members>Account.AccountGroupIdChanged</members>
        <name>WorkflowAlert</name>
    </types>
    <types>
        <members>Account.PutNewValueInOldGroupIdField</members>
        <members>Account.SetGroupIdUpdated</members>
        <members>Account.UnsetGroupIdUpdated</members>
        <name>WorkflowFieldUpdate</name>
    </types>
    <types>
        <members>Account.AccountGroupIdChanged</members>
        <members>Account.GroupIdUpdatedSet</members>
        <name>WorkflowRule</name>
    </types>
    <version>35.0</version>
</Package>

./objects/Account.object

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    <fields>
        <fullName>Group_ID_Updated__c</fullName>
        <defaultValue>true</defaultValue>
        <externalId>false</externalId>
        <label>Group ID Updated</label>
        <trackFeedHistory>false</trackFeedHistory>
        <trackHistory>false</trackHistory>
        <type>Checkbox</type>
    </fields>
<!--<fields>
        <fullName>Group_ID__c</fullName>
        <externalId>false</externalId>
        <label>Group ID</label>
        <length>18</length>
        <required>false</required>
        <trackFeedHistory>false</trackFeedHistory>
        <trackHistory>false</trackHistory>
        <type>Text</type>
        <unique>false</unique>
    </fields>  -->
    <fields>
        <fullName>Old_Group_ID__c</fullName>
        <externalId>false</externalId>
        <label>Old Group ID</label>
        <length>18</length>
        <required>false</required>
        <trackFeedHistory>false</trackFeedHistory>
        <trackHistory>false</trackHistory>
        <type>Text</type>
        <unique>false</unique>
    </fields>
</CustomObject>

./email/unfiled$public/AccountGroupIdChanged.email

The Group ID of {!Account.Name} has changed from: 
{!Account.Old_Group_ID__c}
to 
{!Account.Group_ID__c}

./email/unfiled$public/AccountGroupIdChanged.email-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<EmailTemplate xmlns="http://soap.sforce.com/2006/04/metadata">
    <available>true</available>
    <description>Account Group Id Changed</description>
    <encodingKey>UTF-8</encodingKey>
    <name>AccountGroupIdChanged</name>
    <style>none</style>
    <subject>Group ID Changed:  {!Account.Name}</subject>
    <type>text</type>
</EmailTemplate>

./workflows/Account.workflow

<?xml version="1.0" encoding="UTF-8"?>
<Workflow xmlns="http://soap.sforce.com/2006/04/metadata">
    <alerts>
        <fullName>AccountGroupIdChanged</fullName>
        <ccEmails>email@email1.com</ccEmails>
        <ccEmails>email@email2.com</ccEmails>
        <ccEmails>email@email3.com</ccEmails>
        <description>AccountGroupIdChanged</description>
        <protected>false</protected>
        <senderType>DefaultWorkflowUser</senderType>
        <template>unfiled$public/AccountGroupIdChanged</template>
    </alerts>
    <fieldUpdates>
        <fullName>PutNewValueInOldGroupIdField</fullName>
        <field>Old_Group_ID__c</field>
        <formula>Group_ID__c</formula>
        <name>PutNewValueInOldGroupIdField</name>
        <notifyAssignee>false</notifyAssignee>
        <operation>Formula</operation>
        <protected>false</protected>
    </fieldUpdates>
    <fieldUpdates>
        <fullName>SetGroupIdUpdated</fullName>
        <field>Group_ID_Updated__c</field>
        <literalValue>1</literalValue>
        <name>SetGroupIdUpdated</name>
        <notifyAssignee>false</notifyAssignee>
        <operation>Literal</operation>
        <protected>false</protected>
        <reevaluateOnChange>true</reevaluateOnChange>
    </fieldUpdates>
    <fieldUpdates>
        <fullName>UnsetGroupIdUpdated</fullName>
        <field>Group_ID_Updated__c</field>
        <literalValue>0</literalValue>
        <name>UnsetGroupIdUpdated</name>
        <notifyAssignee>false</notifyAssignee>
        <operation>Literal</operation>
        <protected>false</protected>
    </fieldUpdates>
    <rules>
        <fullName>AccountGroupIdChanged</fullName>
        <actions>
            <name>AccountGroupIdChanged</name>
            <type>Alert</type>
        </actions>
        <actions>
            <name>SetGroupIdUpdated</name>
            <type>FieldUpdate</type>
        </actions>
        <active>true</active>
        <formula>NOT ( ISNEW() ) &amp;&amp;  ISCHANGED( Group_ID__c )</formula>
        <triggerType>onAllChanges</triggerType>
    </rules>
    <rules>
        <fullName>GroupIdUpdatedSet</fullName>
        <actions>
            <name>PutNewValueInOldGroupIdField</name>
            <type>FieldUpdate</type>
        </actions>
        <actions>
            <name>UnsetGroupIdUpdated</name>
            <type>FieldUpdate</type>
        </actions>
        <active>true</active>
        <criteriaItems>
            <field>Account.Group_ID_Updated__c</field>
            <operation>equals</operation>
            <value>True</value>
        </criteriaItems>
        <triggerType>onAllChanges</triggerType>
    </rules>
</Workflow>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.