Determine What You Should Test for Apex
Learning Objectives
After completing this unit, you’ll be able to:
- Describe the key benefits of Apex unit tests.
- Define a class with test methods.
- Execute all test methods in a class and inspect failures.
Apex Unit Tests
The Apex testing framework enables you to write and execute tests for your Apex classes and triggers on the Salesforce Platform. Apex unit tests ensure your Apex code is high quality and meets the requirements for deploying Apex.
The Apex testing framework makes it easy to test your Apex code. Apex code can only be written in a sandbox environment or a Developer org, not in production. Apex code can be deployed to a production org from a sandbox. App developers can also distribute Apex code to customers from their Developer orgs by uploading packages to the Salesforce Platform AppExchange. In addition to being critical for quality assurance, Apex unit tests are also requirements for deploying and distributing Apex.
Apex unit tests lead to better code as it:
- Ensures that your Apex classes and triggers work as expected.
- Has a suite of regression tests that can be rerun every time classes and triggers are updated to ensure that future updates you make to your app don’t break existing functionality.
- Meets the code coverage requirements for deploying Apex to production or distributing Apex to customers via packages.
- Delivers high-quality apps to the production org, which makes production users more productive.
- Delivers high-quality apps to package subscribers, which increases your customers’ trust.
Code Coverage Requirement for Deployment
Before you can deploy or package your code for the Salesforce Platform AppExchange, at least 75% of Apex code must be covered by tests, and all those tests must pass. In addition, each trigger must have some coverage.
Increase Your Code Coverage
When writing tests, try to achieve the highest code coverage possible, including positive and negative test cases, and bulk and single-record processing. Don’t just aim for 75% coverage, which is the lowest coverage that the Salesforce Platform requires for deployments and packages. The more test cases that your tests cover, the higher the likelihood that your code is robust.
Sometimes, even after you write test methods for all your class methods, code coverage is not at 100%. One common cause is not covering all data values for conditional code execution. For example, some data values tend to be ignored when your class method has if
statements that cause different branches to be executed based on whether the evaluated condition is met. Ensure that your test methods account for these different values.
Let’s look at an example. This example includes the class method, getTaskPriority
, which contains two if
statements. The main task of this method is to return a priority string value based on the given lead state. The method validates the state first and returns null if the state is invalid. If the state is CA, the method returns 'High'; otherwise, it returns 'Normal' for any other state value.
public with sharing class TaskUtil { public static String getTaskPriority(String leadState) { // Validate input if(String.isBlank(leadState) || leadState.length() > 2) { return null; } String taskPriority; if(leadState == 'CA') { taskPriority = 'High'; } else { taskPriority = 'Normal'; } return taskPriority; } }
This is the test class for the getTaskPriority
method. The test method simply calls getTaskPriority
with one state ('NY').
@IsTest private class TaskUtilTests { @IsTest static void testTaskPriorityNormal() { //GIVEN String state = 'NY'; //WHEN Test.startTest(); String priority = TaskUtil.getTaskPriority(state); Test.stopTest(); //THEN Assert.areEqual('Normal', priority); } }
After running this test, the code coverage for TaskUtil
is 75%. That’s not bad, but we can do better.
The reason our code coverage is at 75% is because our test class didn’t contain a test to pass an invalid state parameter. Similarly, line 11 wasn’t covered because the test method didn’t pass 'CA' as the state.
Let’s look at two more test methods to cover those scenarios. The following shows the full test class after adding the testTaskPriorityHigh
and testTaskPriorityInvalid
test methods.
@IsTest private class TaskUtilTests { @IsTest static void testTaskPriorityNormal() { //GIVEN String state = 'NY'; //WHEN Test.startTest(); String priority = TaskUtil.getTaskPriority(state); Test.stopTest(); //THEN Assert.areEqual('Normal', priority); } @IsTest static void testTaskPriorityHigh() { //GIVEN String state = 'CA'; //WHEN Test.startTest(); String priority = TaskUtil.getTaskPriority(state); Test.stopTest(); //THEN Assert.areEqual('High', priority); } @IsTest static void testTaskPriorityInvalid() { //GIVEN String state = 'Montana'; //WHEN Test.startTest(); String priority = TaskUtil.getTaskPriority(state); Test.stopTest(); //THEN Assert.areEqual(null, priority); } }
Rerunning the test classes shows coverage for TaskUtil
is now at 100%!
Other Things to Consider
Now that you have a better understanding of why we test our code, keep the following in mind when testing your Apex code.
- You can save up to 6 MB of Apex code in each org. Test classes annotated with
@IsTest
don’t count toward this limit. - Even though test data rolls back, no separate database is used for testing. As a result, for some sObjects that have fields with unique constraints, inserting duplicate sObject records results in an error.
- Test methods don’t send emails.
- Test methods can’t make callouts to external services. You can use mock callouts in tests.
- SOSL searches performed in a test return empty results. To ensure predictable results, use
Test.setFixedSearchResults()
to define the records to be returned by the search.