fflib-apex-common
fflib-apex-common copied to clipboard
Test Data Class
I use this library in many different orgs and as such I find that there are quite a few different instances where field requirements are put in place. Whenever I newly install this package or have to update it, I have to remember to manually update every place an account, contact, and activity are used rather than just updating a test data class.
Any thoughts on whether is makes sense or not to work on adding this functionality?
Can you elaborate more on what you mean by 'manually update every place an account, contact and activity are used rather than update a test data class' ? Is this because you have validation rules in your org on these objects that cause the tests to fail? If so, yes i get it, a single Test Data Class would be a good idea.
@tmowbrey For creating records, I've had great success using @dhoechst TestFactory
@afawcett As an example, in fflib_SObjectSelectorTest, there are multiple methods using the same account data setup information. When I install it in one org (or update), I have to add BillingCountry to each method, copy+pasting each time. In another org I have to add BillingState and Billing Country. While not a large issue, the copy+paste leads me to believe that this information should be consolidated into a separate class for data generation to make updating test data easier when required. I think adding in functionality similar to the package @daniel-huckins has mentioned would help enforce some best practices when using the library as well as help increase the usability of the library.
Thoughts?
Yeah got it, sounds good, though i think there are plenty of good libraries doing this, in this case, for better internally management of test data can we just factor into a single test data setup class perhaps, that would address your need and not add to much more bloat to the library (i'm hoping to get round to review thoughts elsewhere in this repo to modularising this at somepoint).
I am currently arguing with myself the pro and cons of:
-
Use the library "as-is", making it easy to maintain but leaving questions about long term implementation and extending functionality. I'll refer to this option as "Fostering"
-
Removing the
fflib_
prefix and making the library our own. Allowing us to customize without some fancy extending of the classes, and also providing clarity in our repository (when using 3rd party library I'd like to refer to it as 3rd party package and not manage it in our codebase). I'll refer to this option as "Adopting"
This issue is actually one of the conflicts that has me feeling more confident about option 2 in the long run. We have validation rules that cause the DML operations in the test to fail, so I can't see around modifying the test directly use my Forge.cls
which handles the org's validations rules when creating an object instance.
I guess in your opinion when is it valid to "Adopt" the library and maintain it manually verses "Fostering" it as a 3rd party library within our codebase?
@Xtremefaith i totally misunderstood your original question here. I now understand your issue about different org shapes and validations impacting the tests for the library.
I think the best way to approach this is to use more mocking and/or making the tests more agnostic if possible. Can you get the ball rolling with like a top 3 most common test failures and why?
We can then come up with some ways to either avoid making changes (better more org agnostic tests) or at the very least make your code edits easier via some more centralised approach in the lib.
Hows this sound?
@afawcett Sounds great! So one of my first issues was just getting fflib into my environment. For example fflib_QueryFactoryTest.cls
has multiple instances of inserting a new task. Because my org has a validation rule that requires a certain prefix on the Subject
I now need to add a value to the Subject
field prior to INSERT
. At the moment I have resolved this by creating each object through my Forge
class like so:
Task tsk = (Task) Forge.createSObject('Task');
This class then accounts for all org related validation rules. But the fact that I've now edited code from an external library/repository feels dirty. I feel I either should adopt this codebase as my own and possibly drop the fflib_
prefix or I should just reference this from a managed package where perhaps FFLIB
is the namespace.
At the moment I believe this is the only real conflict, as you mentioned I stub everything else through mocking. I still haven't established our domain model yet as I've just been working through the learning curve of these patterns (studying PEAA from Martin Fowler), ApexMocks, and the best approach to implementing this code base and its future with our own code base and its future.
Update:
Another instance that causes grief is in the ApexMocks fflib_MatcherDefinitionsTest.cls, at line 52 it attempts to insert an SObject
that is an Account
and again we have validation rules that prevent certain characters in the account name because of an integration to QuickBooks (sorry I voted for FinancialForce :P ). So we get the following error:
System.DmlException: Insert failed. First exception on row 0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Account name cannot contain a ":". A colon is a special system symbol used by Quickbooks.: [Name]
Class.fflib_MatcherDefinitionsTest: line 52, column 1
Hope that helps!
Yep this helps, the requirement seems clear now. Some kind of abstraction or perhaps a test plugin means to customize the way standard objects are initialized in tests. So if there was say fflib_ITestHelper interface and the tests looked for a class called say FFLibTestHelper that implemented the interface. The interface would have methods like createTask. The framework could include base impl of the interface you could extend and override. Would something like this work?
NOTE: If you put fflib in a managed package, you will need to make all the classes global and the prefix will be fflib.fflib_SObjectDomain (for example). Also you will inhibit upgrades to the library, since global has constraints that are not neccessarly constraints when evolving the library.
I think this will work, not sure I completely understand what you have in mind but yes the ability to extend/customize the way objects are initialized in tests would be great!
Meanwhile I'm in the Salesforce DX pilot and attempting to package fflib in its own "app" to see if this is a more viable means of "packaging" it within our org's codebase. I think this will work in the long run just not 100% sure how it will all work together just yet but I'll keep you posted.
Excellent, thanks @Xtremefaith for the update. Yes we are also in the SFDX pilot and very keen to explore how libraries can be used in it. Keep us posted on your findings! 👍
@afawcett if you have a chance I posted some findings in the DX chatter group, one specifically on ApexEnterprisePatterns as a separate workspace, but unfortunately when it gets to dependencies questions within DX the forum seems to go stale. I currently have it setup as a workspace, with an AEP
project, and ApexMocks
and fflib
as apps within the project. like so:
- /ApexEnterprisePatterns/AEP/ApexMocks/default/
- /ApexEnterprisePatterns/AEP/FFlib/default/
Again though no confirmation from anyone on their end to determine if this is inline with DX and more importantly Packaging 2.0 when it comes to deployment. I also started a #dx channel on the Salesforce Community Slack Team as a means to chat about some opinions surround DX build outs and deployment strategy.
As of right now I feel really prepared to use this patterns but I'm struggling to force it into a segmented DX structure as I feel it should.
@Xtremefaith i need to check my access on the chatter group, as the link is not working for me. How is the discussion going since your last post above?
@afawcett Since our last post I have been able to put both what I refer to as "Apex Enterprise Patterns" (AEP), and ApexMocks into their own artifacts within my org's code repository like so:
As you can see as long as I define each package (or "artifact") within the sfdx-project.json like so then when I push to a scratch org or convert using the force:source:convert then it will compile the metadata accordingly and put it in the org properly (bar any name collisions, hence I keep the prefixes as they are).
Note: You can see that in this way I can separate the default files from the test without an issue, and I can maintain AEP and ApexMocks separately as you have or collectively if desired. When I put this in a new org I get 93% code coverage out of the box
As you can see from the first screenshot, I intend to separate regional development (US, UK, AU, etc.) in separate artifacts (possibly package 2.0 packages) that will depend on everything within core which includes FFLib. Hope that update helps some!
This is very cool! @phardakerffdc FYI
Has there any developments with this issue? I just came across the issue again in another org where a a specific field was set to required required on Task/Events and now I have 9 fflib_
test failing. So naturally I'm again questioning the best practice for now.