In this article, we’ll explore best practices for Apex Test classes in Salesforce, accompanied by illustrative examples. Feel free to review our in-depth session recording on Test classes for a more comprehensive understanding. If you’re new to Test classes, be sure to consult this post for insights and examples on getting started.
Apex Test Class Best Practices
To craft effective apex test cases, developers should follow these guidelines and write efficient test classes:
- @isTest Annotation: Initiate your test class with the @isTest annotation to designate it as a test class.
- One Assert Statement per Method: Implement the new assert class for both negative and positive tests within each method.
-
- Examples:
Assert.areEqual(condition, msg)
Assert.areEqual(expected, actual, msg)
Assert.areNotEqual(expected, actual, msg)
- Examples:
-
- @TestSetup Usage: Utilize the @TestSetup annotation to create test records in a method, making them accessible in every test method within the class.
- TestFactory Class: Create a TestFactory class with the @isTest annotation to exclude it from organizational code size limits.
- Avoid SeeAllData=True: Always set
(seeAllData = false)
at the class or method level. Certain entities like User, Profile, Organization, AsyncApexJob, CornTrigger, RecordType, ApexClass, ApexComponent, and ApexPage can be accessed without (seeAllData=true). - Avoid Hard-Coding IDs: Refrain from using hard-coded IDs in test classes or any Apex class.
- System.runAs for Record Sharing: Due to Apex running in system mode where permission and record sharing are not considered, enforce record sharing using System.runAs.
- Governor Limits: Utilize
Test.startTest()
to acquire a new set of governor limits during the act stage of the test.Test.stopTest()
allows a return to the previous governor limits. - Testing Exceptions: Test any exceptions caught in production methods by providing test data that triggers the exception. Assert the exception type and error message.
- Exercise Bulk Trigger Functionality: Test the functionality of bulk triggers by incorporating a minimum of 200 records in your tests.
- Code Coverage: Finally, strive for at least 75% code coverage for production deployment, but target a higher threshold of 90% or more. The emphasis should not solely be on code coverage percentages; instead, ensure comprehensive coverage of use cases, encompassing positive, negative, bulk, and single record scenarios.
Creating Test Data
Let’s explore the methods for generating test data for Salesforce test classes.
-
Manual Creation for Each Test
The visibility of a test method doesn’t matter, so declaring a test method as public or private doesn’t make a difference as the testing framework is always able to access test methods. For this reason, the access modifiers are omitted in the syntax.
Test methods must be defined in test classes, which are classes annotated with
@isTest
. This sample class shows a definition of a test class with one test method.
@isTest private class MyTestClass { @isTest static void myTest() { // code_block } }
2. Create Test Data Factory class
@isTest public with sharing class TestDataFactory{ /* createAccount method */ public static Account createAccount(Boolean doInsert){ Account acc = new Account(); acc.Name = 'SFMonday Test Account'; if(doInsert){ insert acc; } return acc; } /* createContact method */ public static Contact createContact(Boolean doInsert){ return createContact(doInsert, createAccount(true).Id); } public static Contact createContact(Boolean doInsert, Id accId){ Contact con = new Contact(); con.AccountId = accId; con.FirstName = 'Salesforce'; con.LastName = 'Monday'; con.Email = 'SalesforceMonday@salesforcemonday.com'; if(doInsert) { insert con; } return con; } /* createOpportunity method */ public static List<Opportunity>createOpportunity(Id accountId, Integer oppsNum) { List<Opportunity> opps = new List<Opportunity>(); for(Integer i = 1; i <= oppsNum; i++) { Opportunity opp = new Opportunity(); opp.name = 'Account ' + i; opp.accountId = accountid; opp.amount = 10000; opp.closeDate = Date.today().addDays(3); opp.stageName = 'Prospecting'; opps.add(opp); } return opps; } }
Pingback: Exciting Updates to SOQL in the Latest Salesforce Release - SalesforceMonday