by Aurélien Laval, April, 2015

When deploying your Apex development from a sandbox to a production environment (Apex trigger, Apex class, etc.), Salesforce requires 75% code coverage, and the execution of that code must not incur any development errors. This article will show how to develop an Apex test class to cover your Apex development, and ensure the code performs correctly. Specifically, this article will show how to:

  1. Develop an Apex trigger that will run itself during the creation and modification of a Case record.
  2. Create a test class to test the trigger code.
  3. Test a web service call and simulate its response.

Getting Started

If you don't have one already go to https://developer.salesforce.com/signup and sign up for your Developer Edition (DE) account. For the purposes of this example, I recommend signing up for a Developer Edition even if you already have an account. This ensures you get a clean environment with the latest features enabled. Then, navigate to http://login.salesforce.com to log into your developer account.

I am using the Force.com IDE. Based on the Eclipse platform and built on the Tooling API, the Force.com IDE provides a comfortable environment for programmers familiar with integrated development environments, letting you code, compile, test, package, and deploy all from within the IDE. Follow the instructions to install Eclipse and the Force.com IDE.

Developing Your Apex Trigger

You could use any Apex code example, but I decided to create an Apex trigger. An Apex trigger is Apex code that is executed before and after:

  • the creation of a record;
  • modification of a record; or
  • deletion of a record.

Keep in mind the following Salesforce limitations:

  • To deploy your Apex development in a production organization, the maximum number of SOQL (Salesforce Object Query Language) queries allowed during a transaction is 100, so the best practice is to think bulk. Don’t query one record by one, but group them in one query while using an Apex trigger. For example, if you have 50 records or more, add all of them in a list and query it (… WHERE Id IN).
  • For performance reasons, a transaction must not exceed 10 seconds of CPU time (back office side). Because of this limitation, there is no time to access the database. The time is just for Apex classes, Apex triggers, etc. You can see the time of a transaction with the Limits.getCpuTime() method.

Now let's continue with the process of the Apex trigger. The example will:

  1. Iterate over the Case list to get the Email field of all records that are not linked to a contact (empty AccountId field) but that have the SuppliedEMail field filled.
  2. Ask Salesforce to get contacts that are the same email addresses obtained in the Case list.
  3. Iterate over the Case list again to get the Email field of all records that are not linked to a contact, but that have the SuppliedEMail field filled.
  4. If the contact has the same email address as a case, we will link the case to the contact. If not, we will create a contact with the case email address and link that case to the contact.

Here is the Apex trigger code:

trigger JoinContactToCase on Case (before insert, before update) {

	public map<String, Contact> contactsMap = new map<String, Contact>();
	public map<String, Contact> createdContacts;
	public List<String> mailsList = new List<String>();
	public List<Contact> contactToCreate = new List<Contact>();
	public List<Integer> remainderCases = new List<Integer>();
	Integer caseNumber = 0;
   
	// For each case
	for(Case myCase : Trigger.new){
  	 
   	// If the contactId field is null and the suppliedEMail field is right
   	if(myCase.ContactId == null && myCase.SuppliedEmail != null){
      	 
       	// Getting the email address
       	mailsList.add(myCase.SuppliedEmail);
   	}  	 
	}
   
	// Getting contacts linked to the email addresses
	contactsMap = getContacts(mailsList);
   
	// For each case
	for(Case myCase : Trigger.new){
  	 
   	if(myCase.ContactId == null && myCase.SuppliedEmail != null){
      	 
       	// If the contact with email address is in the map
       	if(contactsMap.containsKey(myCase.SuppliedEmail)){
           		myCase.ContactId = contactsMap.get(myCase.SuppliedEmail).Id;
       	}
       	// We add the contact in the list to create it later
       	else{
           	contactToCreate.add(new Contact(
               	Email = myCase.SuppliedEmail,
               	LastName = myCase.SuppliedEmail
           	));
          	 
           	// We specify what case we have to link to a contact
           	remainderCases.add(caseNumber);
       		}
   	}
  	 
   	caseNumber++;
	}
   
	// Creation of the contacts
	insert contactToCreate;
   
	// Getting contacts created in a map<email, contact>
	createdContacts = getContactsByMap(contactToCreate);
   
	// For each case which is not linked to a contact yet	
for(Integer theCaseNumber : remainderCases){
  	 
   	// If the contact is just created
   	if(createdContacts.containsKey(Trigger.new[theCaseNumber].SuppliedEmail)){
      	 
       	// Linking to the contact
       	Trigger.new[theCaseNumber].ContactId = createdContacts.get(Trigger.new[theCaseNumber].SuppliedEmail).Id;
   	}
	}
   
	// Return contacts linked to the email addresses given like parameters
	public map<String, Contact> getContacts(List<String> mails){
  	 
   	return getContactsByMap([
       	SELECT Id, Email
       	FROM Contact
       	WHERE Email IN :mails
   	]);
	}
   
	// Return contacts of the given like parameters with a map
	public map<String, Contact> getContactsByMap(List<Contact> contactsList){
   	map<String, Contact> result = new map<String, Contact>();
  	 
   	for(Contact myContact : contactsList){
       		result.put(myContact.Email, myContact);
   	}
  	 
   	return result;
	}
}


Testing Your Apex Trigger

When you deploy your code in a production environment, your Apex code has to cover a minimum of 75% of the code with unit tests and without errors. It is a best practice to have a code cover nearly 100% to be sure of the process and avoid bugs. When you are testing your development, you'll need to reproduce an environment with mock data and run your Apex trigger. For this example, we need:

  • A Case list
  • A Contact list (to cover the unit test where a case record has the same email address as a contact)

There are two unit tests to consider:

  • The email address corresponds to an existing contact
  • The email address does not correspond to an existing contact

Below is the test class for the example Apex trigger:

@isTest(seeAllData=false)
private class JoinContactToCaseTest {

	public static List<Contact> contactsList;
	public static List<Case> casesList;
	public static String email1;
   
	static void init(){
   	contactsList = new List<Contact>();
   	casesList = new List<Case>();
  	 
   	email1 = 'toto@toto.com';
  	 
   	casesList.add(new Case(
       		SuppliedEMail = email1
   	));
	}

	/** Test with an existing contact **/
	static testMethod void testWithExistingContact() {
   	init();
   	Test.startTest();
  	 
   	contactsList.add(new Contact(
       	Email = email1,
       	LastName = email1
   	));
   	insert contactsList;
  	 
   	insert casesList;
  	 
   	// Getting the case to verify that the ‘ContactId’ field is not null
   	casesList = [
       	SELECT Id, ContactId
       	FROM Case
       	WHERE Id = :casesList[0].Id
   	];
  	 
   	// Verification
   	System.assertEquals(casesList[0].ContactId, contactsList[0].Id);
  	 
   	Test.stopTest();
	}
   
	/** Test with a non-existent contact **/
	static testMethod void testWithDoesntExistingContact() {
   	init();
   	Test.startTest();
  	 
   	insert casesList;
  	 
   	// Getting the case to verify the ‘ContactId’ field is not null
   	casesList = [
       	SELECT Id, ContactId
       	FROM Case
       	WHERE Id = :casesList[0].Id
   	];
  	 
   	// Getting the contact created with the Apex trigger
   	contactsList = [
       	SELECT Id, Email
       	FROM Contact
       	WHERE Email = :email1
   	];
  	 
   	// Verification
   	System.assertEquals(casesList[0].ContactId, contactsList[0].Id);
  	 
   	Test.stopTest();
	}
}

In the code above, the first line indicates the test class. The parameter, seeAllData=false informs the system we don’t want to use data stocked into the organization. So, if I query contacts, I will have only the data created in the test class, not in the organization. The seeAllData parameter is optional, so you can only write the @isTest annotation. The default value is false, however. So, you'll have to specify seeAllData=true, if you want the actual data.

A caveat to this is that you have to write seeAllData=true when you use role or priceBook, for example. You can’t query them if the parameter is equal to false because they don't exist. You can see that the class is private but that's not very important. An Apex test class can be declared public, private (private is better), or global, but a controller can only be public or global (depending on what you want to do in your code). Also, you can declare an Apex class in public (or private) and specify the sharing with *** with a sharing class or *** without sharing class (*** is equal to public or private). By default, if you declare your Apex class public (or private), it is similar to declaring it public with sharing, so you respect the sharing rules (hierarchy) of your organization.

The Method Init

static void init(){ 
   contactsList = new List<Contact>(); 
   casesList = new List<Case>(); 
        
   email1 = 'toto@toto.com'; 
        
   casesList.add(new Case( 
       SuppliedEMail = email1 
   )); 
} 

To factor the code, I use the init() method, which is called at the beginning of each test method. This method initializes the data (inserts cases and contacts). It is easier to modify one value in one place, than one value in three places.

Let's move on to the two unit tests we'll use to test the Apex trigger with two cases. The case where a contact record has the same email address (SuppliedEmail field) and the case where the SuppliedEmail field of a case record doesn’t correspond to a contact record.

First Test Case

/** Test with an existing contact **/ 
static testMethod void testWithExistingContact() { 
   init(); 
   Test.startTest(); 
        
   contactsList.add(new Contact( 
       Email = email1, 
       LastName = email1 
   )); 
   insert contactsList; 
        
   insert casesList; 
        
   // Getting the case to verify that the ‘ContactId’ field is not null
   casesList = [ 
       SELECT Id, ContactId 
       FROM Case 
       WHERE Id = :casesList[0].Id 
   ]; 
        
   // Verification
   System.assertEquals(casesList[0].ContactId, contactsList[0].Id); 
        
   Test.stopTest(); 
}

First, initialize the test environment by calling the init() method. Then use the Test.startTest() and Test.stopTest() methods to specify what will be executed between them, and execute Salesforce's governor limits (limitations of SOQL queries in a transaction, runtime of a script, etc). For these reasons, the initialization of the variables must be done first in the init() method.

Next the example creates a contact and a case (after the contact) for the Apex trigger previously developed. The created case has to be joined to the contact. To be sure and verify it, we get the cases after the Apex trigger is executed.

Note: Before inserting the cases, the ID field must equal NULL, but after line 12, they are filled in by Salesforce so I can query them (line 18) using this field. Make sure you query your records after you create them to be sure they are not null.

The last step is to verify the results using the System.assertEquals() method. The first parameter will be the expected value, and in the second parameter is the resulting value (line 22). you should verify that the ContactId field of the case has the same value as the ID field of the created contact.

If you are using the Force.com IDE plugin for the Eclipse IDE, you will see this if your Apex code cover is correct:


Apex1.png


Unfortunately, if your Apex code cover is bad (occurred error or less than 75%), you will see this:


Apex2.png


For this unit test, I edited the verification line (line 22). The expected value is replaced by “Bla bla.” So, the ContactId field of the case doesn’t look like “Bla bla” => error.

The Apex trigger we are testing names JoinContactToCase.


Second Test Case

/** Test with a non-existent contact **/ 
static testMethod void testWithANonExistentContact() { 
   init(); 
   Test.startTest(); 
        
   insert casesList; 
        
   // Getting of the case to be sure the “ContactId” field is not null
   casesList = [ 
       SELECT Id, ContactId 
       FROM Case 
       WHERE Id = :casesList[0].Id 
   ]; 
        
   // Getting of the contact created by the Apex trigger
   contactsList = [ 
       SELECT Id, Email 
       FROM Contact 
       WHERE Email = :email1 
   ]; 
        
   // Verification
   System.assertEquals(casesList[0].ContactId, contactsList[0].Id); 
        
   Test.stopTest(); 
}

This second unit test allows us to verify that when the SuppliedEmail field of a case doesn’t correspond to the Email field of a contact, the Apex trigger creates one and joins the case to the contact.

The scenario of this unit test is similar to the first. Create a case, but here, don’t create a contact since the Apex trigger will do it for us.

The verification is identical to the first unit test. You want to be sure the case is linked to the contact with the SuppliedEmail field equal to the Email field of the case.

There are a lot of methods you can use to verify value or execute tests with specific conditions. For example, to verify that two values are identical, you can use the System.assertEquals() method, but if you want to verify that the two values are different, use the System.assertNotEquals() method. To run an exception, run your test inside a try catch block and get the error to verify it in the catch.


Testing A Web Service

If you call a web service, you have to develop an Apex test class to test and simulate its response. To help, you will need to use something that mocks data. You won't be testing the web service itself, but rather the data that it returns -- if the web service is down during the test, the deployment to the production environment won't work. So, you can create fake (mock) data and use it to test the development.

Testing With A Mock Response

To develop a mock response, we will reset the web service response when it is parsed by the Salesforce WSDL2Apex component.

To know the structure you have to reset, I recommend consulting the generated classes by Salesforce when you have imported your WSDL file by the WSD2L component.

The object will be a Result type. The web service in this tutorial returns only a mathematical operation result type, Double, so resetting the data is simple.

It is easy to constitute a mock response:

  1. Create a class that implements the interface WebServiceMock.
  2. Develop a DoInvoke() method that needs several parameters inside of it.
  3. Use one variable (response) to stock your mock data.

Below is the Apex test class used to test the web service:

@isTest
global class CalculatorWebServiceMockDivide implements WebServiceMock {

  global void doInvoke(
  	 Object stub,
  	 Object request,
  	 Map<String, Object> response,
  	 String endpoint,
  	 String soapAction,
  	 String requestName,
  	 String responseNS,
  	 String responseName,
  	 String responseType) {
  		 calculatorWebService.divideResponse_element respElement = new calculatorWebService.divideResponse_element();
  		 respElement.Result = 5.0;
  		 response.put('response_x', respElement);
   }
}

First, we specified that the Apex class is an Apex test class with the @isTest annotation, and we created an object that represents the type of response for the class of the web service. Then, we initialized a response and gave it to the Result attribute. To finish, we gave our Result object, with the key response_x, to the variable response.

This is everything you need to create a mock response in Salesforce. Here's how to use this response in an Apex test class.

static testMethod void testWithMock() {
   Test.startTest();

   Double numerator = 10.7;
   Double denominator = 12.9;

   // Replace the response of the web service by the mock response
   Test.setMock(WebServiceMock.class, new CalculatorWebServiceMockDivide());

   // Call the web service
   MyWebServiceCalculatorCallout myWebServiceCalculatorCallout = new MyWebServiceCalculatorCallout();

   Test.stopTest();

   // Verification
   System.assertEquals(5.0, myWebServiceCalculatorCallout.divide(numerator, denominator));
}

The method is similar to the previous one. It uses the Test.startTest() and Test.stopTest() methods at the beginning and end of the test method to delineate what is tested. What is different is calling the Test.setMock() method because here, we give to it the WebServiceMock interface in the first parameter, and a context of the mock object in the second parameter. It means from now on, each call to the web service only returns the mock response that we gave above (whatever the context, and it is why I gave wrong numbers to my method because the returned value is stocked inside the static resource: 10.7 / 12.9 != 5).

To finish, we want to be sure that the final result is the good value: 5. When the test is finished, we verify that everything is good:


Apex3.png


Testing With A Static Resource

We can also use a static resource for testing a web service call.

static testMethod void testWithStaticResource(){

   Test.startTest();

   Double numerator = 10.7;
   Double denominator = 12.9;

   // Creation of the mock response
   StaticResourceCalloutMock mock = new StaticResourceCalloutMock();

   // Using the static resource divideResult
   mock.setStaticResource('divideResult');

   // We specify that responses of a web service will be like the HttpCalloutMock class
   Test.setMock(HttpCalloutMock.class, mock);

   MyWebServiceCalculatorCallout myWebServiceCalculatorCallout = new MyWebServiceCalculatorCallout();

   // Verification
   System.assertEquals(5.0, myWebServiceCalculatorCallout.divide(numerator, denominator));

   Test.stopTest();
}

Here, we created a StaticResourceCalloutMock object and gave it to the name of the static resource we wanted to use to simulate the web service response. To finish, we used the Test.setMock() method, and gave it the HttpCalloutMock class in the first parameter and the mock in the second parameter.


About the Author

Aurélien is a Salesforce technical consultant in France. He likes to write articles to share his knowledge with people. You can follow him on Twitter at @AurelienLaval (https://twitter.com/AurelienLaval).