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:
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:
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:
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.