19

How do I write a unit-test / test class for a trigger?

What are my options?

Inline with the trigger?

New Class?

28

Apex Trigger Code Testing

  • Writing test code to invoke Apex Trigger logic is a requirement, even if you have other tests that cover other aspects of the code called from it, such as utility or library methods in other Apex classes.
  • As such you must have at least one Apex test perform the require DML operations on the object to invoke the trigger logic, no matter how little code maybe in your trigger.
  • It is common best practice these days to keep code in your Apex Trigger to a minimum and call out to another Apex class. As such it enables you to test the code in a more detailed and more focused mannor in other tests without having to setup a full data set required to issue DML requests in such tests. It also gives rise to better factoring and encapsulation as your code base grows. This consideration is popular with the TDD methodology.
  • As has already been commented you need to place test code outside of the trigger, indeed it is not actually possible to put test code in a trigger anyway.

The following is very basic illustration of the above, it strays a little into some best practices on layering and encapsulating logic around objects, but I think is relevant here, as its important to think about all logic.

Apex Class

/**
 * Class encapsulates logic relating to the Account object, 
 *   other Account related logic required by VF controllers, batch jobs etc could be placed here
 */
public with sharing class Accounts {    

    public static void onBeforeInsert(List<Account> accounts) { }

    public static void someOtherAccountFunctionality(List<Account> accounts) { }    
} 

Apex Trigger

trigger AccountTrigger on Account (before insert) {

    // Delegate the trigger work to an Apex class that encapsulates behavior relating to the Account object
    if(Trigger.isInsert)
        Accounts.onBeforeInsert(Trigger.new);
}

Test Class

@IsTest
private with sharing class AccountsTest {

    private static testmethod testAccountTriggerViaDML()
    {
            // This example is simple, illustrates how to invoke the trigger code via DML (required), 
            //   but can become complex and detract from TDD and more granularly testing of the Accounts class
            Account testAccount = new Account( Name = 'Test Account' );
            insert testAccount;
            testAccount = [select Id, Name from Account where Id = :testAccount.Id];
            System.assertEquals(testAccount.Name, 'Test Account');  
    }

    private static testmethod testAccountsOnInsertDirectly()
    {
            // This example is simple, but illustrates the pattern gives access to the Account class code without going via DML, 
            //   more TDD flexibility here (though must be peformed in conjunction with the above style tests as well)
            Account testAccount = new Account( Name = 'Test Account' );
            Accounts.onBeforeInsert(new List<Account> { testAccount } );
            System.assertEquals(testAccount.Name, 'Test Account');
    }

    private static testmethod testSomeOtherAccountFunctionality()
    {
            // Test a specific method on the Accounts class (this may also be tested via the tests associated with the logic calling it
            //     however in a TDD approach these tests still have a role to test such methods in more detail)
            Account testAccount = new Account( Name = 'Test Account' );
            Accounts.testSomeOtherAccountFunctionality(new List<Account> { testAccount });
            System.assertEquals(testAccount.Name, 'Test Account');
    }
}
6

You can write test methods in two different ways one is in same class and the other one is to write the test methods in separate class the first method is only applicable for the Apex classes having Api version 27 or earlier and the second one is to write test methods in a separate class which is also a good practice please visit http://forcespider.wordpress.com/ for more details.

4

http://blogs.developerforce.com/engineering/2013/04/apex-test-code-segregation.html

Here is the recent blog post from Josh Kaplan (Product Manager for APEX).Starting with the Summer ’13 release of the platform, Apex test methods need to be created in test classes.This change will make Testing efficient.Various reasons are clearly documented why we need different class in the above blog.

2

A few pointers from my experiences with writing test cases for Triggers:

  1. Its always good to have a separate test Utility class where you can add new test methods for testing your triggers. Add the name of the trigger as a comment above your test class and vice versa to make it easier to maintain code.

  2. For those wondering how to write test cases for triggers, remember that your trigger is activated by the operation on the object that its written against(i.e Insertion, update, delete, upsert). So if your trigger is supposed to fire after an insert or update, you would need to create and update some records of that object in your test class. I found this step by step tutorial very helpful when learning this for the first time. http://shivasoft.in/blog/salesforce/step-by-step-salesforce-tutorial-%E2%80%93-creating-trigger-and-test-cases-%E2%80%93-6-of-6/

  3. You know that you need to strive for 100% code coverage. The tricky part in achieving this is testing the exception part of your trigger's code. See this for ideas: http://boards.developerforce.com/t5/Apex-Code-Development/How-do-I-test-for-an-exception-addError-in-a-Trigger/td-p/145443

  4. I have faced problems because of the location of Test.Starttest() and Test.stopTest() in my test methods especially with Triggers. Its best to set up all your data first and then surround your DML operations in between StartTest() and StopTest().

1

All your test methods must be included in classes marked as @isTest (to avoid maximum size of the apex code in your org).

The internal test need to be converted to the extra test class.

public class SomeClass {
    static testMethod void someTest() {
        // Test logic
    }
}

The someTest method is now a SomeTest class:

@isTest
private class SomeTest {
   // Test logic
}

P.S. maybe not really relevant for the trigger testing but anyway good to know.

0

Adding some points here

  1. Create TestDataFactory(Class name can be anything you want!) Utility class to have methods for creating test for different objects. So its easier to add field values/updates required for validations in future.

Add @isTest to TestDataFactory class

  1. Wherever any test class is being created use methods from TestDataFactory class to get test records.

  2. These methods need not to insert data, but return the list of records which we can insert in testsetup, below is example of one such method and test class.

Below methods returns list of Accounts, size as specified by count

public static Account[] getAccounts(Integer count){
    Account[] accontList = new List<Account>();
    for(Integer i = 0; i < count; i++){
        Account acc = new Account(Name ='Test Account' + i); // required data need to filled.
        accountList.add(acc);
    }
    return accounList;
}

This can be used in test class like

    @TestSetup
    static void makeData(){
        // create account
        List<Account> accounts = TestDataFactory.getAccounts(1);
        insert accounts;
//...

You can create methods for other object as required.

protected by Community Jun 1 '17 at 19:37

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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