How do I write a unit-test / test class for a trigger?
What are my options?
Inline with the trigger?
New Class?
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It only takes a minute to sign up.
Sign up to join this communityHow do I write a unit-test / test class for a trigger?
What are my options?
Inline with the trigger?
New Class?
Apex Trigger Code Testing
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 {
@IsTest
static void 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');
}
@IsTest
static void 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');
}
@IsTest
static void 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');
}
}
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.
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.
A few pointers from my experiences with writing test cases for Triggers:
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.
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/
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
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().
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.
Adding some points here
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
Wherever any test class is being created use methods from TestDataFactory
class to get test records.
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.