48

Premise

I want to load Accounts and their related Contacts using Test.loadData() and StaticResources. How do I do this given that the Contact records must have the ID of their parent Account yet the CSV file containing the Contacts can't know in advance what the Account's ID will be?

SFDC Help KB has one solution using Cases and CaseComment and there is an Idea here.

The SFDC KB article uses legitimate ID values (18 chars) in the parent CSV's ID column and child CSV's parentId column. The child CSV parentId column must reference the same ID as used for its intended parent.

But clearly the parentId in the CSV won't be the real ID of the inserted parent SObject. So, can the ID values be arbitrary as long as they are unique?

3
  • I was always annoyed that Test.loadData performed the insert and didn't hand back an in-memory data set (so that I might set up the relationships prior to DML) - now we know why! Thank you! Profound insight and profound question! Commented Oct 28, 2014 at 21:43
  • Probably should be in Q&A format, but I approve this content.
    – sfdcfox
    Commented Oct 28, 2014 at 21:47
  • yes, i thought of that and can convert to Q&A once I feel the assertion in the question does not turn out to have a community-discovered flaw
    – cropredy
    Commented Oct 28, 2014 at 21:50

2 Answers 2

42

Moving question into answer

And the answer appears to be yes (a tip to a colleague of mine who discovered this) and it works for master-detail relationships too

The Proof

  • Account
  • Contact with lookup relationship AccountId and custom lookup Alternate_Account__c

Here's the CSV file for the Account - note the values of 0 and 1 for the test Account.ID rows: enter image description here

And here's the CSV for the Contacts - note the values in the AccountId column and Alternate_Account__c column reference the artificial IDs of Account CSV rows:

enter image description here

Using this Apex testmethod, I loaded the Accounts data from the static resource first and then the Contacts data from its static resource. I displayed the constructed SObject relationships using System.debug:

@isTest
private class TestStaticResourceDataLoad {
    private static void testStaticResourceDataLoadMasterDetail() {
        List<SObject> aList = Test.loadData(Account.sObjectType,'testAccounts');
        List<SObject> cList = Test.loadData(Contact.sObjectType,'testContacts');

        String res = '';
        for (Account a: [select id, name, (select id, firstname, lastName,email, 
                                 reportsTo.lastname, alternate_account__r.name from Contacts) 
                             from Account where id IN :aList]) {
            String cRes = '';
            for (Contact c: a.contacts)
                cRes += '\n...name= ' + c.firstName + ' ' + c.lastName + ' ' + c.email + 
                             ' altAcct=' + c.alternate_account__r.name;
            res += '\n account id/name: ' + a.id + ' ' + a.name + ' w/ contacts: ' + cRes;
        }
        System.debug(LoggingLevel.INFO,'res='+res);
    }
}

And here are the results:

13:11:43.657 (3657616477)|USER_DEBUG|[15]|INFO|res=
 account id/name: 001m0000007pN6hAAE 00AccountName w/ contacts: 
   name= 0.0fname 0.0lname [email protected] altAcct=01AccountName
   name= 0.1fname 0.1lname [email protected] altAcct=01AccountName
 account id/name: 001m0000007pN6iAAE 01AccountName w/ contacts: 
   name= 1.0fname 1.0lname [email protected] altAcct=00AccountName

The Contacts are appropriately parented and the Contact.alternate_account__c lookup is also accurate.

Side note - what does not work is references within a StaticResource dataset. For example, I tried to use the Contact.ReportsToId field to have one Contact be a child of another in the same batch as shown here:

enter image description here

This isn't surprising as the Sobjects are inserted as a batch and the intra-Sobject reference is not resolvable yet. No different than using DataLoader.

Side note 2 Another use case I couldn't get to work via this approach was loading PricebookEntry rows for the standard pricebook unless you used the actual ID of the standard Pricebook as you can't insert a standard pricebook via testmethods. This can have some portability issues

Summary

What is striking to me is that across Apex statement executions (the successive Test.loadData(..) lines), SFDC keeps track of a mapping between real IDs of the loaded Sobject and the fake ID you specify in the CSV file so you can build relationships. None of this is documented in the Apex Developer's Guide and the casual developer could easily assume that Test.loadData(..) can only load top level SObjects or otherwise force the developer to post-process and add the relationships later.

5
  • side note 4 - it is allowed to use same Id for different object records, meaning both account record and contact record can use 1 in Id field.
    – Xi Xiao
    Commented Nov 13, 2018 at 12:12
  • side note 3 - load two datasets for same object will result in "Validation Errors While Saving Record"
    – Xi Xiao
    Commented Nov 13, 2018 at 13:17
  • Personally, I feel they should support import of data with relationships in much the same way from uploaded CSV files or uploaded CSV file bundles (ZIPs) - by uploaded I mean uploaded at the time of data import, rather than as a static resource.
    – Phil W
    Commented Mar 14, 2019 at 18:20
  • @XiXiao I just tried loading two datasets for the same custom object and that works these days. I also included a self lookup relationship in these two files and it was populated correctly. (I couldn't get the lookup to work when using only one file.)
    – Kasper
    Commented Sep 25, 2020 at 17:20
  • Side note 5 - the fake IDs need not be integers; any string seems to work. This allows you to use existing Ids from an org export, or alternate values like product codes. Commented Apr 25, 2022 at 16:40
2

For the Side Note: Its is valid. There is no way to populate self lookups. But that can be feasible if SFDC can start supporting batch number as parameter to this method Test.loadData ( even in that case first record will be left out )

It is a good observation to see that ID field can be manipulated and relationship between the parent and child can be maintained. I think Salesforce should update this behavior as it is a key feature to use the full capability of Test.loadData method. Missing in documentation has resuilted in this post for Fake Fields https://help.salesforce.com/apex/HTViewSolution?id=000167032&language=en_US Actually if documentation is added for this then workaround of the above post is not needed.

You can check this and see if it deserves to vote this idea https://success.salesforce.com/ideaView?id=08730000000kxHhAAI

Thanks Amit

2
  • 1
    I believe for self-lookups we could use external id fields
    – Patlatus
    Commented Jan 5, 2017 at 12:07
  • I just tried loading two datasets for the same custom object and that works these days. I also included a self lookup relationship in these two files and it was populated correctly. (I couldn't get the lookup to work when using only one file.)
    – Kasper
    Commented Sep 25, 2020 at 17:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .