Testing Apex That Relies on Field History Tracking?

Question
I have Apex code that queries the history records of a custom object using field history tracking. This works fine in a real environment, but when I run my unit tests, no history records are created, causing my tests to fail.
How can I test code that depends on field history tracking in Apex?
Answer
Testing Apex code that relies on field history tracking can be challenging because history records are not created during test execution. However, you can work around this by using Test.loadData() to insert history records, injecting test data into methods instead of querying in tests, or structuring your code to separate history retrieval from processing. These approaches ensure that your test logic remains functional while avoiding reliance on unavailable system-generated history records.
CRS Info Solutions offers expert Salesforce online training with real-time projects, certification guidance, interview coaching, and a job-ready approach. Enroll for free demo today!!!
Field history tracking records are not automatically created during test execution. However, there are a few ways to work around this limitation:
1. Using Test.loadData() to Insert History Records
You can use Test.loadData() to create history records in a test. This approach requires a CSV file uploaded as a static resource. The CSV should contain at least the ParentId field, and you can manually specify OldValue and NewValue.
Example CSV (TestCircuitHistoryData):
ParentId,Field,OldValue,NewValue
a0R000000000000,Card__c,123,456
a0R000000000000,Card__c,456,abc Test Class:
@isTest
static void testLoadHistory() {
// Load history records from the static resource
List<Circuit__History> historyRecords = Test.loadData(Circuit__History.SObjectType, 'TestCircuitHistoryData');
// Verify that OldValue and NewValue are populated in memory
System.debug(historyRecords);
}Explanation: The testLoadHistory method is an Apex test that loads historical records for a custom object using Test.loadData(), which reads data from a static CSV resource named 'TestCircuitHistoryData'. This allows the test to simulate field history tracking by inserting history records into memory, even though querying them would return null for OldValue and NewValue. The System.debug(historyRecords); statement verifies that the loaded records contain the expected data during test execution.
2. Injecting Test Data Instead of Querying in Code
Since querying history records in a test returns null for OldValue and NewValue, it’s best to separate data retrieval from processing. This allows history data to be passed into methods manually.
public void queryAndProcessHistory() {
List<Circuit__History> historyRecords = [SELECT Id, ParentId, Field, OldValue, NewValue FROM Circuit__History WHERE ParentId IN :myCircuits];
processHistory(historyRecords);
}
public void processHistory(List<Circuit__History> historyRecords) {
for (Circuit__History ch : historyRecords) {
if (ch.Field == 'Special_Field__c' && ch.OldValue != 'trouble') {
// Perform necessary logic
}
}
}Explanation: The queryAndProcessHistory method retrieves history records for specific circuits and passes them to processHistory, which handles the logic. In processHistory, each record is checked for a specific field (Special_Field__c), and if its OldValue is not “trouble,” some operation is performed. This separation of querying and processing allows for better testability, as test data can be injected into processHistory without needing actual history records in the database.
Test Class:
@isTest
private class HistoryTest {
static List<Circuit__History> circuitHistory;
static void setup() {
circuitHistory = Test.loadData(Circuit__History.SObjectType, 'TestCircuitHistoryData');
}
@isTest
static void testProcessHistory() {
MyCircuitClass obj = new MyCircuitClass();
// Inject test history data
Test.startTest();
obj.processHistory(circuitHistory);
Test.stopTest();
}
}Explanation: This test class verifies the functionality of processHistory() in MyCircuitClass by injecting predefined history records using Test.loadData(). The setup() method loads test data from a static resource, ensuring consistent inputs. The test method testProcessHistory() starts a test context, invokes processHistory() with the injected history data, and stops the test to simulate real execution conditions.
Summing Up
Testing Apex code that relies on field history tracking requires workarounds since Salesforce does not create history records during tests. Using Test.loadData() allows inserting test history records, but querying them will return null for OldValue and NewValue, so separating retrieval from processing is crucial. By injecting test data into methods, you can ensure proper assertions and maintain test reliability.
Master Salesforce with Expert Training in Pune
Elevate your career with our Salesforce training program in Pune, designed for aspiring Admins, Developers, and AI professionals. Our expert-led certification guidance ensures you’re equipped with the knowledge and skills needed to excel in the Salesforce ecosystem.
Our training combines in-depth theory with practical, hands-on sessions to help you bridge the gap between learning and real-world application. Salesforce training in Pune With personalized support, advanced interview preparation, and industry-focused tools, we prepare you to confidently tackle the challenges of a competitive job market.
Don’t miss this opportunity—sign up for a free demo class today and take the first step toward a thriving career in Salesforce!!!
Related Posts:
Salesforce Field History Tracking
How to Test Apex code with Field History Tracking?

