mattaddy / SObjectFabricator

An SObject fabrication API to reduce database interactions and dependencies on triggers in Apex unit tests.
Other
91 stars 14 forks source link

Documentation / installation / readme points #2

Open cropredyHelix opened 7 years ago

cropredyHelix commented 7 years ago

I like where you are going here and might make a couple of suggestions

  1. You should follow the fflib class naming convention and prefix all these classes with sfab_ 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.
  2. 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 like OwnerId, WhatId, or WhoId as the code-under-test is using the getSobjectType() method and these will break on strings such as Opp-1
  3. The second argument to the FabricatedSObject construct is of type Type, but I wonder if it would be more natural for it to be an SobjectType? I can't think of a time where I have ever written Account.class but often would use Account.SobjectType.
  4. 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.
  5. 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 fabricated OpportunityLineItem to its Product2 via PricebookEntryId. That is, the mocked Selector is returning fabricated Sobjects. In our shop, real world tests involve having to mock Account + Contact + Order + OrderItem(s) + Product2(s) + PricebookEntry(s) + Pricebook2. This is not an uncommon use case.
  6. 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})
mattaddy commented 7 years ago

Thanks for all the suggestions!

  1. I thought about this as well, and plan to rename the classes in my next commit.
  2. I can certainly update the readme to use generated ids instead of the 'Opp-1' style.
  3. Interesting! You're right in that I've typically passed SObjectTypes around rather than Types, but is there an inherent benefit here?
  4. Can you elaborate on this point more? I would love to provide more context in the readme to increase potential adoption, but want to make sure I understand first.
  5. I agree, coupling SObject fabrication with a selector layer provides excellent benefits. Are you suggesting that we update the readme to include a section on how to incorporate it with selectors?
  6. Mocking more than one level deep is definitely a big plus as well. We should definitely call this out in the readme as well.

Again, thanks for all the suggestions! Feel free to submit a pull request for any of these recommendations, otherwise I'll likely implement them very soon.

cropredyHelix commented 7 years ago

Matt. I appreciate the response.

3 - I like xxx.SObjectType solely for the familiarity and consistency with other code, notably fflib.

4 - if you are unit testing a method that updates sobject xxxx, you can't fabricate that object as the code-under-test DML will fail. Similarly, if you fabricate an Account sobject and the code under test tries to insert an Opportunity or Case or Contact, the code will fail because the lookupId won't exist in the database.

The way around this is to use the fflib pattern, unit of work layer, and Apex Mocks. Andy Fawcett's second edition Force.com Enterprise Architecture has a chapter on this.

Most apex code ultimately does DML so blending (in the readme) how to execute DML on fabricated objects would be useful to the user of your lib.

5 Yes.

Bottom line, whereas you are an advanced developer, getting tradition-bound apex devs to use your excellent lib requires a few more examples.

Once you commit a new version with prefixes, I know I'll be adding it to our org!

mattaddy commented 7 years ago

@cropredyHelix I merged #3 which addresses the prefix suggestion.

cropredyHelix commented 7 years ago

cool - this should increase adoption. The more you tie this into the fflib pattern via the readme, the more likely you can get andyinthecould to feature you as a guest blogger which will increase the number of users of this fine fabrication framework - as I noted earlier, the more than 2 levels for child relationships is really great!

On Sun, Aug 27, 2017 at 11:15 AM, Matt Addy notifications@github.com wrote:

@cropredyHelix https://github.com/cropredyhelix I merged #3 https://github.com/mattaddy/SObjectFabricator/pull/3 which addresses the prefix suggestion.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mattaddy/SObjectFabricator/issues/2#issuecomment-325214978, or mute the thread https://github.com/notifications/unsubscribe-auth/AYdgA_RzJkvR6eGUm_LTviTD8ehwO21lks5scbI-gaJpZM4O8DHt .

-- Eric Kintzer Salesforce Architect 1 Circle Star Way, Floor 2, San Carlos, CA 94070 650 533 5619 (m) www.helix.com | We're hiring https://www.helix.com/careers

Mrmattmann commented 5 years ago

We've loved using this and I agree, more than 2 levels for child relationships is amazing! We use this along with the fflib pattern and they have a class called fflib_IDGenerator that we use to generate an ID for whatever object we are fabricating. Used with the Mocking as mentioned above makes testings much faster!