Last active
December 23, 2025 14:25
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @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