in apex trigger ~ read.

Testing Apex triggers

One of the questions that I see frequently asked is “how do I unit test my trigger?”. Salesforce requires your Apex classes to have 75% coverage and your triggers to have 1% coverage before you're allowed to deploy or package it. That’s a great measure to encourage developers to create automated tests for their business logic.

Unfortunately, some developers only create tests that cover enough lines of code to keep Salesforce happy, and completely disregard assertions. Remember that the main reason to test your code is to verify that your business logic works! When you modify your code tomorrow (you know you will!), your unit tests will help you verify that you haven’t broken your previous behaviour and will reduce the number of calls from upset customers.

If you were using test-driven-development, you would write your unit tests first, and let that drive the implementation of your trigger. In practical terms, I have seen that many developers and admins tend to write the trigger first and think about tests later. I will take this situation as my starting point.

A sample trigger

Let’s assume that your company wants to make sure that any new leads created in the system are assigned a task to remind the owner to call them within 2 days.

You could implement this with a workflow rule, but since you want to learn how to test your Apex triggers, you decide to go for a trigger instead. After giving this some thought, you come up with the following trigger.

Note how the triggers is bulkified so that it can deal with updates consisting of multiple records.

So how do I test it?

Triggers are fired by performing DML operations on certain records. Bearing this in mind, the way to create test coverage is:

  1. Identify test cases
    • Generally, there is one test case per branch of your trigger (ie: every "if" generates two branches)
    • Find the kind of input that will fire this test case
    • There is usually at least one invalid or "no operation" case
    • It's good practice to have a bulk test case, to check that your trigger is properly bulkified
  2. Create a test class
  3. Create a test method for each test case identified above
    • In the test method(s), perform the DML operation that would fire your trigger and the specific test case
    • In every test method, assert that the actions worked

Identify test cases

In our example there are two test cases: inserting a new lead and the bulk test case. There is no branching in the code, and there is no invalid or "no operation" case. All we need is a test class with two test methods: one that will insert a lead and verify that the followup task is created, and one that does the same operation in bulk.

Create a test class

It's generally a good practice to identify your classes with some sort of prefix. Naming them "Test" + the name of the class you're testing is a good practice, but any convention that works for your team is good.

We'll name our class TestCreateInitialCallTaskTrigger

Create test methods

The methods will ensure that the task is created, so we'll name them TestInitialCallTaskIsCreated and TestBulkCreationCreatesTasks. The purpose of the bulk test is to insert a large number of records that could cause a non-bulkified trigger to go over DML limits. Since the current limits are set to 150 DML operations, we will insert 151 and verify that the trigger still works to ensure it's properly bulkified.

Perform the DML operation

All we need to do is insert a lead. At this point our test class looks like this:

Verify that the actions worked

This is where asserts come into play. We need to verify that one and only one task has been created, and that the fields are populated correctly.

Our test class would look like this:

In the first method, we have 4 asserts for a single test case. That is OK. We need to verify that all aspects of the requirement are met; in this case, the task being created, the due date, etc.

Note that the third argument in System.AssertEquals() is optional. I use it to tell my future self what is wrong with the code. When one of my changes breaks the assert in 3 months' time, I want to be able to see at a glance what went wrong.

Testing additional cases

That is all good and well for the simple trigger, but what to do when things start getting more complex? Often, triggers will have conditional logic and perform different actions depending on the state of the item that triggers them.

Let's take this example one step forward. Let's assume that the business wants to create a further task when the lead has been contacted, to send information by email. That is, unless the lead has been converted, in which case the sales agent can set up the details of the task in the lead conversion screen.

After giving this some thought, you come up with this trigger.

In this example, there are 3 test cases:

  • Create or update a Lead with status 'Not Contacted': create the 'initial call' task
  • Create or update a Lead with status 'Contacted': create the 'followup task'
  • Convert a lead: do nothing regardless of the status

And there is also one "no action" case:

  • Set the status to other than 'Contacted' or 'Not Contacted': no action

And the bulk case:

  • Insert 151 records and ensure that the tasks are created

We have identified 5 test cases, so we need one test class with 5 test methods. Each of these needs to create or update a lead with a particular status. Our class could look like this:

I have also created a helper method to convert a lead to keep the code easier to read. The helper method does not need the @isTest annotation.

Conclusion

We have seen that a trigger can be quite simple to test. The first thing to do is identify all the test cases, and then find a way to exercise each of them. A DML operation is enough to fire the trigger, and assigning different value to different fields of the object will allow us to test different cases. Real-life triggers can get more complex, but the idea behind the process is the same. Good luck!