Open
Description
I like where you are going here and might make a couple of suggestions
- You should follow the
fflib
class naming convention and prefix all these classes withsfab_
or somesuch so the classes don't conflict with any other classes in one's org and are easier to track for exclusion purposes in Test Suites. - Instead of using examples of IDs as 'Opp-1', use your own version of
fflib_IdGenerator.generate(Opportunity.SobjectType)
- this way you get Ids of the proper key prefix. This becomes important when mocking polymorphic relationship fields likeOwnerId
,WhatId
, orWhoId
as the code-under-test is using thegetSobjectType()
method and these will break on strings such asOpp-1
- The second argument to the FabricatedSObject construct is of type
Type
, but I wonder if it would be more natural for it to be anSobjectType
? I can't think of a time where I have ever writtenAccount.class
but often would useAccount.SobjectType
. - When combined with the fflib UnitOfWork pattern and ApexMocks, your fabricated objects can even be unit tested to see if they were updated as expected (all but the DML). Adding to your ReadMe to show how this could be done would avoid the natural inclination of most developers to reject such a fabrication framework because the mocked Sobjects can't be updated and most code that gets unit tested does updates.
- The framework becomes even more useful when coupled with the
fflib
Selector layer so code-under-test can mock the results going from, say, a fabricatedOpportunityLineItem
to itsProduct2
viaPricebookEntryId
. That is, the mocked Selector is returning fabricated Sobjects. In our shop, real world tests involve having to mockAccount + Contact + Order + OrderItem(s) + Product2(s) + PricebookEntry(s) + Pricebook2
. This is not an uncommon use case. - Your framework does something I at first thought was impossible, but it can mock more than one level deep! This is super useful. It needs calling out in the ReadMe. See the code below
FabricatedSObject fabricatedAccount = new FabricatedSObject(Account.class); // mock Acct
fabricatedAccount.setField(Account.Id, 'AId-1');
FabricatedSObject fabricatedCase = new FabricatedSObject(Case.class); // mock Case
fabricatedCase.setField(Case.Id, 'CsId-1');
FabricatedSObject fabricatedCaseComment = new FabricatedSObject(CaseComment.class); // mock CaseComment
fabricatedCaseComment.setField(CaseComment.Id, 'CsCId-1');
fabricatedAccount.setChildren('Cases', new List<FabricatedSObject> {fabricatedCase});
fabricatedCase.setChildren('CaseComments',new List<FabricatedSobject> {fabricatedCaseComment});
Account acct = (Account)new SObjectFabricator().fabricate(fabricatedAccount);
System.debug(LoggingLevel.INFO,'acct='+acct);
System.debug(LoggingLevel.INFO,'cases='+acct.Cases);
System.debug(LoggingLevel.INFO,'caseComment on Case[0]='+acct.Cases[0].CaseComments);
with results:
acct=Account:{Id=AId-1}
cases=(Case:{Id=CsId-1})
caseComment on Case[0]=(CaseComment:{Id=CsCId-1})
Metadata
Metadata
Assignees
Labels
No labels