Skip to content

Instantly share code, notes, and snippets.

@zanstaszek9
Last active December 23, 2025 14:25
Show Gist options
  • Select an option

  • Save zanstaszek9/6695e1e4e74222c3adb5d461a1eeffa4 to your computer and use it in GitHub Desktop.

Select an option

Save zanstaszek9/6695e1e4e74222c3adb5d461a1eeffa4 to your computer and use it in GitHub Desktop.
Apex ParentWithNestedChildrenUtility test class. Creates an in-memory SObject with nested related (child) records using JSON serialization and deserialization, without DMLs.
@IsTest
public class ParentWithNestedChildrenUtility {
/**
* @description Creates an in-memory SObject with nested related (child) records using JSON serialization and deserialization, without DMLs.
* @param parentRecord The parent SObject record to receive children.
* @param childrenRecords SObject records to be nested under the parent.
* @param relationshipFieldName The relationship field name on Parent ('Contacts', 'Opportunities', 'Equipment_Maintenance_Items__r'). Using String, as "SObjectField" type cannot be used due to relationship fields on parent-side are not available that way.
* @param populateFakeIds If true, fake Ids will be generated and assigned to parent and child records when their Ids are null.
* @return The deserialized parent SObject with nested children records.
*/
public static SObject createParentWithNestedChildren(final SObject parentRecord, final List<SObject> childrenRecords, final String relationshipFieldName, final Boolean populateFakeIds) {
if (populateFakeIds && parentRecord.Id == null) {
parentRecord.Id = getFakeId(parentRecord.getSObjectType());
}
final Map<String, Object> parentPopulatedFields = new Map<String, Object>(parentRecord.getPopulatedFieldsAsMap()); // Must be re-initialized with constructor , or we will get "System.FinalException: Collection is read-only" when putting children collection later
final List<Object> childrenPopulatedFields = new List<Object>();
for (SObject child : childrenRecords) {
if (populateFakeIds && child.Id == null) {
child.Id = getFakeId(child.getSObjectType());
}
childrenPopulatedFields.add(child.getPopulatedFieldsAsMap());
}
final Map<String, Object> mandatoryRelationshipStructure = new Map<String, Object>{
'size' => childrenRecords.size(),
'done' => true,
'records' => childrenPopulatedFields
};
parentPopulatedFields.put(relationshipFieldName, mandatoryRelationshipStructure);
final SObject parentWithNestedChildren = (SObject)JSON.deserialize(
JSON.serialize(parentPopulatedFields),
Type.forName(parentRecord.getSObjectType().toString())
);
return parentWithNestedChildren;
}
@IsTest
public static void shouldCreateNestedContactsOnAccount() {
Account parentAccount = new Account(Name = 'Acme Corp Test Account', Industry = 'Technology');
List<Contact> childContacts = new List<Contact>();
childContacts.add(new Contact(
FirstName = 'John',
LastName = 'Smith',
Email = 'john.smith@acme.corp',
Account = parentAccount // Not required, but allows to reference Parent from Child-level
));
childContacts.add(new Contact(FirstName = 'Lucy', LastName = 'Williams', Title = 'VP of Sales'));
Account nestedAccount = (Account) ParentWithNestedChildrenUtility.createParentWithNestedChildren(
parentAccount,
childContacts,
'Contacts',
false
);
Assert.areEqual('Acme Corp Test Account', nestedAccount.Name, 'Account name should not change');
List<Contact> contacts = nestedAccount.Contacts;
Assert.areEqual(2, contacts.size(), 'There should be 2 nested Contacts.');
Assert.areEqual('John', contacts[0].FirstName, 'First Contact first name should not change.');
Assert.areEqual('Technology', contacts[0].Account.Industry, 'Parent field should be accessible from Child, and correct.');
Assert.areEqual('VP of Sales', contacts[1].Title, 'Second Contact title should not change.');
}
//***** Utils ******//
private static Integer fakeIdCounter = 1;
/**
* @author https://foobarforce.wordpress.com/2013/08/15/apex-method-of-the-day-string-repeat/
*/
public static String getFakeId(final Schema.SObjectType sObjectType) {
String result = String.valueOf(fakeIdCounter++);
return sObjectType.getDescribe().getKeyPrefix() + '0'.repeat(12 - result.length()) + result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment