DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Solid Testing Strategies for Salesforce Releases
  • A General Overview of TCPCopy Architecture
  • Modes and Modality in Performance Testing
  • A Guide to Regression Analysis Forecasting in Python

Trending

  • AWS to Azure Migration: A Cloudy Journey of Challenges and Triumphs
  • Memory Leak Due to Time-Taking finalize() Method
  • Infrastructure as Code (IaC) Beyond the Basics
  • The Full-Stack Developer's Blind Spot: Why Data Cleansing Shouldn't Be an Afterthought
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Apex Testing: Tips for Writing Robust Salesforce Test Methods

Apex Testing: Tips for Writing Robust Salesforce Test Methods

Improve your Apex testing skills with strategies for handling test data, managing governor limits, and manipulating time-based logic.

By 
Jaseem Pookandy user avatar
Jaseem Pookandy
DZone Core CORE ·
Mar. 11, 25 · Analysis
Likes (2)
Comment
Save
Tweet
Share
1.7K Views

Join the DZone community and get the full member experience.

Join For Free

Testing is a critical aspect of software development. When developers write code to meet specified requirements, it’s equally important to write unit tests that validate the code against those requirements to ensure its quality. Additionally, Salesforce mandates a minimum of 75% code coverage for all Apex code. 

However, a skilled developer goes beyond meeting this Salesforce requirement by writing comprehensive unit tests that also validate the business-defined acceptance criteria of the application. 

In this article, we’ll explore common Apex test use cases and highlight best practices to follow when writing test classes.

Test Data Setup

The Apex test class has no visibility to the real data, so you must create test data. While the Apex test class can access a few set-up data, such as user and profile data, the majority of the test data need to be manually set up. There are several ways to set up test data in Apex. 

1. Loading Test Data

You can load test data from CSV files into Salesforce, which can then be used in your Apex test classes. To load the test data:

  1. Create a CSV file with column names and values
  2. Create a static resource for this file 
  3. Call Test.loadData() in your test method, passing two parameters — the sObject type you want to load data for and name of the static resource.
Java
 
 list<Sobject> los = Test.loadData (Lead.SobjectType, 'staticresourcename'); 


2. Using TestSetup Methods

You can use a method with annotation @testSetup in your test class to create test records once. These records will be accessible to all test methods in the test class. This reduces the need to create test records for each test method and speeds up test execution, especially when there are dependencies on a large number of records. 

Please note that even if test methods modify the data, they will each receive the original, unmodified test data.

Java
 
@testSetup 
static void makeData() {
	Lead leadObj1 = TestDataFactory.createLead();
    leadObj1.LastName = 'TestLeadL1';
	insert leadObj1;
}
 @isTest
  public static void webLeadtest() {
    Lead leadObj = [SELECT Id FROM Lead WHERE LastName = 'TestLeadL1' LIMIT 1];
    //write your code for test  
  }


3. Using Test Utility Classes

You can define common test utility classes that create frequently used data and share them across multiple test classes. 

These classes are public and use @IsTest annotation. These utility classes can only be used in a test apex context. 

Java
 
@isTest
Public class TestDataFactory {
  public static Lead createLead() {
    // lead data creation
  }
}


System Mode vs. User Mode

Apex test runs in system mode, meaning that the user permission and record sharing rules are not considered. However, it's essential to test your application under specific user contexts to ensure that user-specific requirements are properly covered. In such cases, you can use System.runAs(). You can either create a user record or find a user from the environment, then execute your test code within that user’s context. 

It is important to note that the runAs() method doesn’t enforce user permissions or field-level permissions; only record sharing is enforced. Once the runAs() block completes, the code goes back to system mode. 

Additionally, you can use nested runAs() methods in your test to simulate multiple user contexts within the same test.

Java
 
System.runAs(User){
 // test code 
}


Test.startTest() and Test.stopTest()

When your test involves a large number of DML statements and queries to set up the data, you risk hitting the governor limit in the same context. Salesforce provides two methods — Test.startTest() and Test.stopTest() — to reset the governor limits during test execution.

These methods mark the beginning and end of a test execution. The code before Test.startTest() should be reserved for set-up purposes, such as initializing variables and populating data structures. The code between these two methods runs with fresh governor limits, giving you more flexibility in your test. 

Another common use case of these two methods is to test asynchronous methods such as future methods. To capture the result of a future method, you should have the code that calls the future between the Test.startTest() and Test.stopTest() methods. Asynchronous operations are completed as soon as Test.stopTest() is called, allowing you to test their results.  

Please note that Test.startTest() and Test.stopTest() can be called only once in a test method. 

Java
 
Lead leadObj = [SELECT Id FROM Lead WHERE LastName = 'TestLeadL1' LIMIT 1];

leadObj.Status = 'Contacted';
Test.startTest(); // start a fresh list of governor limits
//test lead trigger, which has a future callout that creates api logs    
update leadObj;
Test.stopTest();

list<Logs__c> logs = [select id from Logs__c limit 1];
system.assert(logs.size()!= 0); 


Test.isRunningTest()

Sometimes, you may need to write code to execute only in the test context. In these cases, you can use Test.isRunningTest() to check if the code is currently running as part of a test execution. 

A common scenario for Test.isRunningTest() is when testing http callouts. It enables you to mock a response, ensuring the necessary coverage without making actual callouts. Another use case is to bypass any DMLs (such as error logs) to improve test class performance. 

Java
 
public static HttpResponse calloutMethod(String endPoint) { 
Http httpReq = new Http();
    HttpRequest req = new HttpRequest();
    HttpResponse response = new HTTPResponse();
    req.setEndpoint(endPoint);
    req.setMethod('POST');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.setTimeout(20000);
    if (Test.isRunningTest() && (mock != null)) {
      response = mock.respond(req);
    } else {
      response = httpReq.send(req);
    }
  
}


Testing With TimeStamps

It's common in test scenarios to need control over timestamps, such as manipulating the CreatedDate of a record or setting the current time, in order to test application behavior based on specific timestamps. 

For example, you might need to test a UI component that loads only yesterday’s open lead records or validate logic that depends on non-working hours or holidays. In such cases, you’ll need to create test records with a CreatedDate of yesterday or adjust holiday/non-working hour logic within your test context.

Here’s how to do that.

1. Setting CreatedDate

Salesforce provides a Test.setCreatedDate method, which allows you to set the createddate of a record. This method takes the record Id and the desired dateTime timestamp. 

Java
 
 @isTest
  static void testDisplayYesterdayLead() {
    Lead leadObj = TestDataFactory.createLead();
    Datetime yesterday = Datetime.now().addDays(-1);
    Test.setCreatedDate(leadObj.Id, yesterday);
    Test.startTest();
    //test your code
    Test.stopTest();
  }


2. Manipulate System Time 

You may need to manipulate the system time in your tests to ensure your code behaves as expected, regardless of when the test is executed. This is particularly useful for testing time-dependent logic. To achieve this:

  1. Create a getter and setter for a nowvariable in a utility class and use it in your test methods to control the current time.
    Java
     
    public static DateTime now {
        get {
    
          return now == null ? DateTime.now() : now;
    
        }
    
        set;
    
      }
    What this means is that in production, when now is not set, it will take the expected Datetime.now(), otherwise, it will take the set value for now.
  2. Ensure that your code references Utility.now instead of System.now() so that the time        manipulation is effective during the test execution.
  3. Set the Utility.nowvariable in your test method. 
    Java
     
    @isTest
    public static void getLeadsTest() {
    
      Date myDate = Date.newInstance(2025, 11, 18);
      Time myTime = Time.newInstance(3, 3, 3, 0);
      DateTime dt = DateTime.newInstance(myDate, myTime);
      Utility.now = dt;
      Test.startTest();
      //run your code for testing
      Test.stopTest();
      
    }


Conclusion

Effective testing is crucial to ensuring that your Salesforce applications perform as expected under various conditions. By using the strategies outlined in this article — such as loading test data, leveraging Test.startTest() and Test.stopTest(), manipulating timestamps, and utilizing Test.isRunningTest() — you can write robust and efficient Apex test classes that cover a wide range of use cases. 

Test data Test method Testing

Opinions expressed by DZone contributors are their own.

Related

  • Solid Testing Strategies for Salesforce Releases
  • A General Overview of TCPCopy Architecture
  • Modes and Modality in Performance Testing
  • A Guide to Regression Analysis Forecasting in Python

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: