Writing an Apex Test Class

Everybody likes to post code that does cool stuff, but so rarely do they post the test code to get proper coverage. I usually don’t post test code either, mostly because I want people to understand the code and write the test class to be able to get it into production. Therein lies the problem. If few people are posting test code, how do you know how to write it? In this post, I’ll walk you through writing a test class for the trigger I used in my previous post, Mixing Workflow Rules and Triggers Part 2. Before you read this post, take a minute to read Apex Testing Best Practices.

First thing to look at is the various paths the code can take. We have the following scenarios that we should test:

  • Insertion of a Hot account with the user’s preference set to send email.
  • Insertion of a Hot account with the user’s preference set to not send email.
  • Update of a Cold account to and Hot account with the user’s preference set to send email.
  • Insertion of a Cold account with the user’s preference set to send email.

I quickly realize that I’m going to be doing several things repeatedly in my tests: insert accounts and change the email preference. Rather than replicate that code in each test, I’ll create a helper method to do it for me.

First, let’s take a look at setting the email preferences. I want to be able to set whether or not the running user’s preference is true or false, so I want to pass the value to the helper from my test method:

53
54
55
56
57
58
59
60
61
62
63
64
// Helper Class to update the user's email preferences
    private static void setEmailPref(Boolean pref) {
        // first get existing preferences
        EmailPref__c ep = EmailPref__c.getInstance();
        // if there aren't preferences set, create a new instance
        if (ep == null) {
            ep = new EmailPref__c();
        }
        // update the preference and upsert it
        ep.Send_Hot_Account_Email__c = pref;
        upsert ep;
    }

Next, here’s the helper method to insert accounts. There are a couple of things interesting here. First, I want to be able to insert both Hot and Cold accounts, so I’ll make that a parameter I can pass. Second, I want to test how my trigger will behave in a bulk context, so I insert 200 accounts.

66
67
68
69
70
71
72
73
74
75
76
    private static List<Account> createAccounts(String rating){
        List<Account> accts = new List<Account>();
 
        for(Integer i = 0; i < 200; i++){
            accts.add(new Account(Name = 'Test ' + i, Rating = rating));
        }
 
        insert accts;
 
        return accts;       
    }

Now we can do our actual tests. Here’s the first scenario. In it, we make sure the email preferences are set to true, reset all the limits for the test, insert our test accounts, query the accounts to get the latest values, and then check to make sure the trigger touched all our inserted accounts. You may be asking why am I checking the Decription? The reason is that I have workflow rule that clears the real value as soon as an email gets sent. Since I can’t catch the record before the workflow rule fires, in my trigger, I check to see if test are running and if so, also update the Description.

4
5
6
7
8
9
10
11
12
13
    private static testMethod void testInsertHotAccounts() {
        setEmailPref(true);
        Test.startTest();
        List<Account> insertedAccts = createAccounts('Hot');
        Test.stopTest();
 
        for (Account a : [select id, Description from Account where id in :insertedAccts]) {
            System.assertEquals('Hot Account Notification Trigger Fired', a.Description);
        }      
    }

Put it all together and here’s the final test class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@isTest
private class HotAccountNotificationTest {
 
    private static testMethod void testInsertHotAccounts() {
        setEmailPref(true);
        Test.startTest();
        List<Account> insertedAccts = createAccounts('Hot');
        Test.stopTest();
 
        for (Account a : [select id, Description from Account where id in :insertedAccts]) {
            System.assertEquals('Hot Account Notification Trigger Fired', a.Description);
        }      
    }
 
    private static testMethod void testInsertHotAccountsNoEmail() {
        setEmailPref(false);
        Test.startTest();
        List<Account> insertedAccts = createAccounts('Hot');
        Test.stopTest();
 
        for (Account a : [select id, Description from Account where id in :insertedAccts]) {
            System.assertEquals(null, a.Description);
        }      
    }
 
    private static testMethod void testInsertColdAccounts() {
        setEmailPref(true);
        Test.startTest();
        List<Account> insertedAccts = createAccounts('Cold');
        Test.stopTest();
 
        for (Account a : [select id, Description from Account where id in :insertedAccts]) {
            System.assertEquals(null, a.Description);
        }      
    }
 
    private static testMethod void testUpdateHotAccounts() {
        setEmailPref(true); 
        List<Account> insertedAccts = createAccounts('Cold');
        for (Account a : insertedAccts) {
            a.Rating = 'Hot';
        }
 
        Test.startTest(); 
        update insertedAccts;
        Test.stopTest();
 
        for (Account a : [select id, Description from Account where id in :insertedAccts]) {
            System.assertEquals('Hot Account Notification Trigger Fired', a.Description);
        }      
    }
 
    // Helper Class to update the user's email preferences
    private static void setEmailPref(Boolean pref) {
        // first get existing preferences
        EmailPref__c ep = EmailPref__c.getInstance();
        // if there aren't preferences set, create a new instance
        if (ep == null) {
            ep = new EmailPref__c();
        }
        // update the preference and upsert it
        ep.Send_Hot_Account_Email__c = pref;
        upsert ep;
    }
 
    private static List<Account> createAccounts(String rating){
        List<Account> accts = new List<Account>();
 
        for(Integer i = 0; i < 200; i++){
            accts.add(new Account(Name = 'Test ' + i, Rating = rating));
        }
 
        insert accts;
 
        return accts;       
    }
}

Mixing Workflow Rules and Triggers Part 2

In an earlier post, I wrote about how I sometimes use workflow rules for manage the logic for my triggers. I sometimes use a similar trick to get workflow rules to fire. Let’s say I have a requirement to send an email to the account owner when something happens on the account, but I want to allow each user to set his or her email preferences. My solution ends up being to use a custom setting to store the user’s email preferences. I can’t set up criteria in my workflow rules to look at the account owner’s custom setting preferences, so I’ll need a trigger to decide if the email needs to be sent. I don’t want to have to write all the logic for sending the email in my trigger. I’d prefer to use the declarative side of things to handle that.

First, I’ll create the custom setting. I’ll make it hierarchical so I can set preferences at the org, profile and user level.

Custom Setting

Second, I’ll add a field on the account that will be used by my workflow rule.

custom field

Third, I’ll add a workflow rule that sends an email and also does a field update to clear the send email field on the account.

workflow rule

Finally, I’ll create my trigger to update the field above which should cause the workflow rule to fire:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
trigger HotAccountNotification on Account (before update, before insert) {
    Account[] AccountsToUpdate = new Account[]{};
    for (Account a : trigger.new) {
        //only send an email if the rating is Hot
        if (a.Rating == 'Hot') { 
            // only send the email if we are inserting a new record or updating the rating on an existing one
            if (Trigger.isInsert || (Trigger.isUpdate &amp;&amp; Trigger.oldmap.get(a.id).Rating != 'Hot')) {
                // Check custom setting to see if the email should be sent
                EmailPref__c ep = EmailPref__c.getInstance(a.OwnerId);
                if (ep.Send_Hot_Account_Email__c) {
                    a.Send_Hot_Email__c = true;
                    // Since there is a workflow rule on this object with a field update 
                    // that clears the send hot email field as soon as it fires,
                    // we can't really assert that the trigger fired.
                    if (Test.isRunningTest()) {
                        a.Description = 'Hot Account Notification Trigger Fired';
                    }
                }
            }
        }
    }
}

You’ll notice one interesting thing about the trigger. On line 16, I use the Test.isRunningTest Method to update another field on the record. The reason I do this is so I can do some proper assertions in my test methods. If I didn’t have this block of code, then the trigger would fire, the field would get updated, the workflow rule would fire and then the field update would reset it back to unchecked. This all happens in the same transaction so I have no way of using an asserting on the Send Hot Email field. Instead, I’m going to update another field only during tests and check to make sure it was updated as expected in my test method.