As a Salesforce Developer, you have encountered this scenario many times:
Your unit tests depend on objects with lookup values populated. This means multiple DML statements during test setup and tests that are often broken when admins change validation rules or process builders.
This will show you how to make tests with complex data more reliable by using Test.loadData to load records that are linked to each other.
How most people use Test.loadData()
You might be using Test.loadData method to load records from Static Resources (Official salesforce docs on Test.loadData), but the official docs don’t explain how to link records.
Most often, developers fix this by doing DML on the records after running Test.loadData(). This, unfortunately, erodes the benefit of even using this test data harness because now you have some test setup data in CSV and some in Apex.
How to fix it: Populate IDs in your test data files
The key to populating relationship fields through Test.loadData method is setting the value of the lookup field to the Id of the linked record. This might sound obvious, but you’re probably wondering “how will I know the id??”.
This is the part that needs a bit of explaining: Test.loadData will accept ID values for your data, so you can know in advance what the IDs will be! Even better, is that they don’t have to be valid salesforce IDs so that you can invent your own IDs (any text) so you can use values that are easier to read for you, the developer. I have used integer values for Ids and they worked well for me.
The main gotcha here is that all IDs must be unique across all static resources in the transaction. If you load Accounts and Opportunities, then you can’t have an Opportunity with ID=1 and Account with ID=1, they must be globally unique IDs.
A Real-World Example
Here is a walk-through to explain the above point further:
Let’s try to understand this with help of an example using the above schema. and say I want to create:
- One Account record.
- One Project record and link this record to the Account record created in step 1.
- Three Milestone records and link these records to the Project record created in step 2.
- Two Milestone Task records for each of the three Milestone records created in Step 3. Also, link them to both the Milestone records from Step 3 and the Project record from Step 2.
Setup Data and Populate Relationship Fields
As we know, we load the data from CSV files uploaded as Static Resources using Test.loadData method, so we need to setup CSV files with data that we will upload as Static Resources.
Here is how the data looks like:
Image 1: Account record with Id field value set an Integer
Image 2: Project record with Account__c lookup set to the Id the record from Image 1
Image 3: Milestone records with Project__c lookup set to the Id of the record from Image 2
Image 4: Milestone Task records having unique Id for each record; Project__c field set to the Id of record from Image 2 and Milestone__c field set to the Id of records from Image 3.
The data shown in the above screenshots are available in this Google Spreadsheet.
Key things to note in the above screenshots:
- All the records (even for different type of objects) have a unique Integer value for Id field.
- Relationship fields are set to Id field values of that particular type of object.
Create Static Resources
The next step is to export the above data into different CSV fields and upload them into your Org as Static Resources.
Here is how it looks like in my dev org:
Loading Test Data
We have setup the data, the relationship fields, and uploaded the CSV files as Static Resources. Now, we can start using this data in our test classes. All the source code including object schema, static resources, and test class is available in this GitHub repo for you to try out in your Org.
We can populate relationship fields with Test.loadData to simplify the apex test setup and make our tests more reliable. We need to make sure that all records (across all different tables) have unique IDs, and make sure the ID is already loaded in a previous step.