Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions sfdx-source/apex-mocks/main/classes/fflib_ApexMocksUtils.cls
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,12 @@ public class fflib_ApexMocksUtils
* values on read-only fields by their name
*/
public static Object setReadOnlyFields(SObject objInstance, Type deserializeType, Map<String, Object> properties) {
JSONParser parser = JSON.createParser((JSON.serialize(objInstance)));
JSONGenerator combinedOutput = JSON.createGenerator(false);

Map<String, Object> mergedMap = new Map<String, Object>(objInstance.getPopulatedFieldsAsMap());
// Merge the values from the properties map into the fields already set on the object
mergedMap.putAll(properties);
// Serialize the merged map, and then deserialize it as the desired object type.
String jsonString = JSON.serializePretty(mergedMap);
return (SObject) JSON.deserialize(jsonString, deserializeType);
// parse the source object and inject new fields into the resulting JSON
streamTokens(parser, combinedOutput, new InjectFieldsEventHandler(properties) );
return JSON.deserialize(combinedOutput.getAsString(), deserializeType);
}

/**
Expand Down Expand Up @@ -183,6 +182,29 @@ public class fflib_ApexMocksUtils
}
}

/**
* Monitors stream events for end of object for the current SObject
* then injects new fields into the stream
*/
private class InjectFieldsEventHandler implements JSONParserEvents
{
Map<String, Object> properties;

public InjectFieldsEventHandler(Map<String, Object> properties) {
this.properties = properties;
}

public void nextToken(JSONParser fromStream, Integer depth, JSONGenerator toStream) {
// Inject children?
JSONToken currentToken = fromStream.getCurrentToken();
if(depth == 1 && currentToken == JSONToken.END_OBJECT ) {
for(String field : properties.keySet()) {
toStream.writeObjectField(field, properties.get(field));
}
}
}
}

/**
* Utility function to stream tokens from a reader to a write, while providing a basic eventing model
*/
Expand Down
43 changes: 43 additions & 0 deletions sfdx-source/apex-mocks/test/classes/fflib_ApexMocksUtilsTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,47 @@ public class fflib_ApexMocksUtilsTest

System.Assert.areEqual(acc.Name, t.What.Name);
}

@isTest
static void setReadOnlyFields_WithChildren_FieldSetSuccessfully() {
// Given
Contact cont = new Contact(
Id = fflib_IDGenerator.generate(Contact.SObjectType),
LastName = 'ContactName'
);

Opportunity opp = new Opportunity(
Id = fflib_IDGenerator.generate(Opportunity.SObjectType),
Name = 'OpportunityName'
);

Account acc = new Account();

Id userId = fflib_IDGenerator.generate((new User()).getSObjectType());

acc = ((List<Account>) fflib_ApexMocksUtils.makeRelationship(
List<Account>.class,
new List<Account>{ acc },
Contact.AccountId,
new List<List<Contact>>{ new List<Contact>{cont} }
))[0];

acc = ((List<Account>) fflib_ApexMocksUtils.makeRelationship(
List<Account>.class,
new List<Account>{ acc },
Opportunity.AccountId,
new List<List<Opportunity>>{ new List<Opportunity>{opp} }
))[0];

// When
Test.startTest();
acc = (Account)fflib_ApexMocksUtils.setReadOnlyFields(
acc,
Account.class,
new Map<SObjectField, Object>{Account.CreatedById => userId}
);
Test.stopTest();

Assert.areEqual(userId, acc.CreatedById);
}
}