Skip to content

Commit

Permalink
Working version
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarParra committed Mar 24, 2022
1 parent b3ca400 commit b3a9782
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .idea/test-data-framework.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified IlluminatedCloud/test_data_framework/OfflineSymbolTable.zip
Binary file not shown.
5 changes: 5 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
* Clean up
* Refactor fflib_UoW to just be what we need
* Clean up System.debugs
* Documentation
* README
* Apexdocs
* Build Apexdocs site
* Limitations and performance
* How many DML statements are used per situation
* In-memory support
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,33 @@ private class IntegrationTests {
System.assertEquals(grandParentAccount.Id, parentAccount.ParentId, 'Expected the relationship to have been correctly created.');
}

@IsTest
private static void supportsDefaultRelationshipBindings() {
Order__c orderRecord = (Order__c)new OrderTestDataBuilder().insertNew();
SObjectTestDataBuilder.commitRecords();

System.assertNotEquals(null, orderRecord, 'Expected the record to have been inserted.');
System.assertNotEquals(null, orderRecord.Customer__c, 'Expected the Customer relationship to have been populated.');
// TODO: Check how many DML statements this is
}

@IsTest
private static void supportsBuildersWithMultipleRelationshipBindings() {
OrderItem__c itemRecord = (OrderItem__c)new OrderItemTestDataBuilder().insertNew();
OrderItem__c itemRecord2 = (OrderItem__c)new OrderItemTestDataBuilder().insertNew();
SObjectTestDataBuilder.commitRecords();

System.assertNotEquals(null, itemRecord.Id);
System.assertNotEquals(null, itemRecord.Order__c);
System.assertNotEquals(null, itemRecord.Product__c);

System.assertNotEquals(null, itemRecord2.Id);
System.assertNotEquals(null, itemRecord2.Order__c);
System.assertNotEquals(null, itemRecord2.Product__c);

System.debug(Limits.getDmlStatements());
}

@IsTest
private static void supportsRecursiveRelationshipsAcrossMultipleLevels() {
Account grandParentAccount = (Account)SObjectTestDataBuilder.of(Account.SObjectType)
Expand Down
File renamed without changes.
24 changes: 24 additions & 0 deletions example/tests/OrderItemTestDataBuilder.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
public with sharing class OrderItemTestDataBuilder extends SObjectTestDataBuilder implements ITestDataBuilder {
public override SObjectType getSObjectType() {
return OrderItem__c.SObjectType;
}

public OrderItemTestDataBuilder with(SObjectField field, Object value) {
return (OrderItemTestDataBuilder) withData(field, value);
}

public OrderItem__c insertNew() {
return (OrderItem__c) this.insertSObject();
}

public List<OrderItem__c> insertNew(Integer numberOfRecords) {
return this.insertSObjects(numberOfRecords);
}

protected override Map<SObjectField, Object> getDefaultValueMap() {
return new Map<SObjectField, Object> {
OrderItem__c.Order__c => bindTo(SObjectTestDataBuilder.of(Order__c.SObjectType)),
OrderItem__c.Product__c => bindTo(SObjectTestDataBuilder.of(Product__c.SObjectType))
};
}
}
5 changes: 5 additions & 0 deletions example/tests/OrderItemTestDataBuilder.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>54.0</apiVersion>
<status>Active</status>
</ApexClass>
23 changes: 23 additions & 0 deletions example/tests/OrderTestDataBuilder.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public with sharing class OrderTestDataBuilder extends SObjectTestDataBuilder implements ITestDataBuilder {
public override SObjectType getSObjectType() {
return Order__c.SObjectType;
}

public OrderTestDataBuilder with(SObjectField field, Object value) {
return (OrderTestDataBuilder) withData(field, value);
}

public Order__c insertNew() {
return (Order__c) this.insertSObject();
}

public List<SObject> insertNew(Integer numberOfRecords) {
return this.insertSObjects(numberOfRecords);
}

protected override Map<SObjectField, Object> getDefaultValueMap() {
return new Map<SObjectField, Object> {
Order__c.Customer__c => bindTo(SObjectTestDataBuilder.of(Account.SObjectType))
};
}
}
5 changes: 5 additions & 0 deletions example/tests/OrderTestDataBuilder.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>54.0</apiVersion>
<status>Active</status>
</ApexClass>
119 changes: 97 additions & 22 deletions force-app/main/default/classes/SObjectTestDataBuilder.cls
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestDataCallback {
private static TestDataBuilderCache builderCache = new TestDataBuilderCache();
private static History testDataHistory = new History();
private static List<LateBinding> allBindings = new List<LateBinding>();

private List<ChildRelationship> registeredRelationships;
protected Map<SObjectField, Object> customValueMap;
private Map<SObjectField, Object> defaultValueMapCached {
Expand Down Expand Up @@ -48,23 +50,58 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD
}

public static void commitRecords() {
fflib_SObjectUnitOfWork uOW = new fflib_SObjectUnitOfWork(
new List<SObjectType>(testDataHistory.getSObjectTypes()),
List<LateBinding> allLateBindingsTemp = new List<SObjectTestDataBuilder.LateBinding>();// TODO: Name better
while (allBindings.size() > 0) {
// Using this technique because throughout this process the allBindings list could be modified
// if more parents need to be added up the chain
LateBinding binding = allBindings.remove(0);
allLateBindingsTemp.add(binding);
SObject parentRecord = binding.builder.insertNew();
binding.setCreatedParent(parentRecord);
}

List<SObjectType> workTypes = getSObjectTypesToRegisterToUnitOfWorkInOrder(allLateBindingsTemp);
System.debug('Types in order: ' + workTypes);

fflib_SObjectUnitOfWork uOW = new fflib_SObjectUnitOfWork(workTypes,
fflib_SObjectUnitOfWork.UnresolvedRelationshipBehavior.AttemptResolveOutOfOrder
);
for (HistoryItem item : testDataHistory.getAllParents()) {
System.debug('Registering parent');

for (HistoryItem item : testDataHistory.getAll()) {
uOW.registerNew(item.getRecord());
}

for (LateBinding binding : allLateBindingsTemp) {
uOW.registerRelationship(binding.childRecord, binding.relationshipField, binding.parentRecord);
}

for (HistoryItem item : testDataHistory.getAllChildren()) {
System.debug('Registering child');
// If any of the records to insert have a relationship we register that as well
// TODO: Add ability to use the relationship that has the External Id and test how many DML operations that uses
uOW.registerNew(item.getRecord(), item.getRelationshipField(), item.getParentRecord());
uOW.registerRelationship(item.getRecord(), item.getRelationshipField(), item.getParentRecord());
}
uOW.commitWork();
}

private static List<SObjectType> getSObjectTypesToRegisterToUnitOfWorkInOrder(List<LateBinding> allLateBindingsTemp) {
List<SObjectType> workTypes = new List<SObjectType>();
for (Integer i = allLateBindingsTemp.size() - 1; i >= 0; i--) {
// Using the reverse order of the parents, as the last records are the top-most parents
SObjectType currentType = allLateBindingsTemp[i].parentRecord.getSObjectType();
if (!workTypes.contains(currentType)) {
workTypes.add(currentType);
}
}

// Registering anything else that might not have been including through bindings (simpler relationships)
for (SObjectType loggedType : testDataHistory.getSObjectTypes()) {
if (!workTypes.contains(loggedType)) {
workTypes.add(loggedType);
}
}
return workTypes;
}

public abstract SObjectType getSObjectType();

public SObject createNew() {
Expand All @@ -82,11 +119,38 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD

public SObjectTestDataBuilder withChild(ITestDataBuilder childBuilder, SObjectField relationshipField) {
// TODO: Ability to insert many children at a time
System.debug('Registering a children rel');
this.registeredRelationships.add(new ChildRelationship(childBuilder, relationshipField));
return this;
}

protected LateBinding bindTo(ITestDataBuilder builder) {
return new LateBinding(builder);
}

public class LateBinding {
private ITestDataBuilder builder;
private SObjectField relationshipField;
private SObject childRecord;
private SObject parentRecord;

public LateBinding(ITestDataBuilder builder) {
this.builder = builder;
}

public LateBinding setRelationshipField(SObjectField field) {
this.relationshipField = field;
return this;
}

private void setChildRecord(SObject childRecord) {
this.childRecord = childRecord;
}

private void setCreatedParent(SObject parentRecord) {
this.parentRecord = parentRecord;
}
}

public virtual void beforeInsert(List<SObject> records) {}
public virtual void afterInsert(List<SObject> records) {}

Expand All @@ -105,6 +169,8 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD

protected List<SObject> insertSObjects(Integer numberOfRecordsToInsert, SObjectField parentRelationship, SObject relatedTo) {
List<SObject> recordsToInsert = createNew(numberOfRecordsToInsert);
this.beforeInsert(recordsToInsert); // TODO: Do this with the callback passing it to the UoW since this is not a real insert
this.afterInsert(recordsToInsert); // TODO: Do this with the callback passing it to the UoW since this is not a real insert
testDataHistory.log(recordsToInsert, relatedTo, parentRelationship);
insertChildren(recordsToInsert);
clear();
Expand All @@ -121,19 +187,38 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD
for (SObjectField defaultField : defaultValueMapCached.keySet()) {
if (!customValueMap.containsKey(defaultField)) {
// Skip any field that has been overridden through the custom value map
instance.put(defaultField, defaultValueMapCached.get(defaultField));
Object fieldValue = defaultValueMapCached.get(defaultField);
if (checkIfBinding(fieldValue, instance, defaultField)) {
continue;
}
instance.put(defaultField, fieldValue);
}
}

for (SObjectField customField : customValueMap.keySet()) {
if (customValueMap.get(customField) != null) {
instance.put(customField, customValueMap.get(customField));
Object fieldValue = customValueMap.get(customField);
if (checkIfBinding(fieldValue, instance, customField)) {
continue;
}
instance.put(customField, fieldValue);
}
}

return instance;
}

private Boolean checkIfBinding(Object fieldValue, SObject record, SObjectField relationshipField) {
if (fieldValue instanceof LateBinding) {
LateBinding binding = (LateBinding)fieldValue;
binding.setRelationshipField(relationshipField);
binding.setChildRecord(record);
allBindings.add(binding);
return true;
}
return false;
}

private void insertChildren(List<SObject> insertedRecords) {
for (SObject insertedRecord : insertedRecords) {
for (ChildRelationship relationship : this.registeredRelationships) {
Expand Down Expand Up @@ -224,12 +309,10 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD
return this.insertedRecordsByType.keySet();
}

public List<HistoryItem> getAllParents() {
public List<HistoryItem> getAll() {
List<HistoryItem> items = new List<HistoryItem>();
for (HistoryItem item : this.getAll()) {
if (item.isParent()) {
items.add(item);
}
for (List<HistoryItem> currentItems : this.insertedRecordsByType.values()) {
items.addAll(currentItems);
}
return items;
}
Expand All @@ -244,14 +327,6 @@ public abstract class SObjectTestDataBuilder implements ITestDataBuilder, ITestD
return items;
}

private List<HistoryItem> getAll() {
List<HistoryItem> items = new List<HistoryItem>();
for (List<HistoryItem> currentItems : this.insertedRecordsByType.values()) {
items.addAll(currentItems);
}
return items;
}

public List<SObject> getChildrenOfByType(Id recordId, SObjectType objectType) {
List<SObject> children = new List<SObject>();
List<HistoryItem> itemsByType = this.insertedRecordsByType.get(objectType);
Expand Down
15 changes: 14 additions & 1 deletion force-app/main/default/classes/fflib_SObjectUnitOfWork.cls
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// FFLIB with order resolution
// TODO: Refactor to only use what is necessary
// TODO: Rename
// TODO: Make IsTest
Expand Down Expand Up @@ -112,6 +113,8 @@ public virtual class fflib_SObjectUnitOfWork
{
public virtual void dmlInsert(List<SObject> objList)
{
System.debug('Doing simple insert on');
System.debug(objList);
insert objList;
}
public virtual void dmlUpdate(List<SObject> objList)
Expand Down Expand Up @@ -329,9 +332,16 @@ public virtual class fflib_SObjectUnitOfWork
assertForNonEventSObjectType(sObjectType);
assertForSupportedSObjectType(m_newListByType, sObjectType);

// List<SObject> sObjectsRegisteredByType = m_newListByType.get(sObjectType);
// if (!sObjectsRegisteredByType.contains(record)) {
// m_newListByType.get(sObjectType).add(record);
// }
System.debug('registering: ' + record);
m_newListByType.get(sObjectType).add(record);
if (relatedToParentRecord!=null && relatedToParentField!=null)

if (relatedToParentRecord != null && relatedToParentField != null) {
registerRelationship(record, relatedToParentField, relatedToParentRecord);
}
}

/**
Expand Down Expand Up @@ -925,6 +935,9 @@ public virtual class fflib_SObjectUnitOfWork
'before child so child does not need an update. In unit of work initialization put ' +
'' + RelatedTo.getSObjectType() + ' before ' + Record.getSObjectType());
}
System.debug('Record: ' + Record);
System.debug('Relationship: ' + RelatedToField);
System.debug('Related To: ' + RelatedTo);
Resolved = false;
} else {
Record.put(RelatedToField, RelatedTo.Id);
Expand Down
2 changes: 1 addition & 1 deletion sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"default": true
},
{
"path": "unpackaged",
"path": "example",
"default": false
}
],
Expand Down

0 comments on commit b3a9782

Please sign in to comment.