Mastering Salesforce Testing: Best Practices and Apex Testing Strategies

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:

  1. @isTest Annotation: Initiate your test class with the @isTest annotation to designate it as a test class.
  2. 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)
  3. @TestSetup Usage: Utilize the @TestSetup annotation to create test records in a method, making them accessible in every test method within the class.
  4. TestFactory Class: Create a TestFactory class with the @isTest annotation to exclude it from organizational code size limits.
  5. 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).
  6. Avoid Hard-Coding IDs: Refrain from using hard-coded IDs in test classes or any Apex class.
  7. 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.
  8. 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.
  9. Testing Exceptions: Test any exceptions caught in production methods by providing test data that triggers the exception. Assert the exception type and error message.
  10. Exercise Bulk Trigger Functionality: Test the functionality of bulk triggers by incorporating a minimum of 200 records in your tests.
  11. 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.

  1. 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; 
  } 
}
3. Create Test Data

In Salesforce, test methods generate records that do not remain permanently in the database; instead, they roll back once the test execution concludes. This automatic rollback feature benefits testing by eliminating the need to manually clean up test data afterward.

By default, Apex tests do not have access to pre-existing organizational data, except for certain setup and metadata objects like User or Profile. It is essential to establish test data specifically for your tests. Creating dedicated test data enhances the resilience of your tests, safeguarding against failures stemming from absent or altered data in the organization. You can craft test data directly within your test method or utilize a utility test class, as detailed later on.

For that reason we create a test data method using @TestSetup annotation:

@TestSetup static void createTestData() { 
  Account acc = TestDataFactory.createAccount(true); 
  Contact cont = TestDataFactory.createContact(acc.id, true); 
  List<Opportunity>oppList = TestDataFactory.createOpportunity(acc.id, 5); 
}
Read Moar!

Testing Apex
Spice Up Your Salesforce Flow With Custom Icons

Share this article...

Salesforce Mentor, with 10 years Salesforce experience, Hardcore Admin & Guru Developer, Geek, Animal lover, and dog & cat rescuer activist. Lifetime student, Stand-Up comedian wannabe, Photographer, Gamer, Anime lover, and coffee addict. Spreading Salesforce love and teaching beyond my capacity. Aiming to become Tech Architect!

One Ping

  1. Pingback: Exciting Updates to SOQL in the Latest Salesforce Release - SalesforceMonday

Leave a Reply

Your email address will not be published. Required fields are marked *