fflib-apex-mocks
fflib-apex-mocks copied to clipboard
Small enhancement to ReadMe
For those of us who didn't come from a Java background and hence weren't exposed to Mockito, the Readme section when() dependency stubbing
is maddeningly tantalizing. The obvious question one immediately asks is what if you don't care what the arg values are in the stubbed method? After much web searching and inspection of fflib_ApexMocksTest
I realized there was a whole wealth of matchers that can be used not only in the verify()
but also in the when()
!
A small additional example in the Readme would inspire and direct the reader to the supported matchers in fflib_Match
.
This framework is pretty powerful stuff but as
- the related SFDC andyinthecloud pages are out of date (ref to generating Mocks class) and
- the second edition book has syntax errors (uses
factory()
method instead ofmock()
method) and - the referenced blog posts are sprinkled with pre Stub API ....
Thus, getting the fflib / andyinthecloud online doc up to date / more comprehensive would go a long way towards increased adoption.
Yes, I agree wholeheartedly. I'll try and sort some of these things out and report back after.
Hi cropredy, I finally got round to working on this.
We could definitely be doing a much better job with our docs, and you're right that it's a major barrier to adoption.
I think part of the problem is that the tests inside ApexMocks are a little contrived. So I put together a little SFDX sample app, with more realistic unit tests.
I'm using the Stub API, matchers, answers, and discuss different techniques for generating mocks and patterns for dependency injection - these are all covered in detail in the readme. https://github.com/dfruddffdc/apex-mocks-sfdx
I'll port over the documentation into this repo's wiki at some point. Until then, hopefully my new sample app will help clarify things.
David - I look forward to diving into this. After reading it, I may need to amend my blog posts on ApexMocks http://cropredysfdc.com/2017/10/11/apex-mocks-and-enterprise-patterns-first-in-a-series/, written after exploiting the technique a few times in our org :-)
Eric
On Sat, Nov 25, 2017 at 11:53 PM, David Frudd [email protected] wrote:
Hi cropredy, I finally got round to working on this.
We could definitely be doing a much better job with our docs, and you're right that it's a major barrier to adoption.
I think part of the problem is that the tests inside ApexMocks are a little contrived. So I put together a little SFDX sample app, with more realistic unit tests.
I'm using the Stub API, matchers, answers, and discuss different techniques for generating mocks and patterns for dependency injection - these are all covered in detail in the readme. https://github.com/dfruddffdc/apex-mocks-sfdx
I'll port over the documentation into this repo's wiki at some point. Until then, hopefully my new sample app will help clarify things.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/47#issuecomment-346990942, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNrckO9bLQuLLn078r4E6j-3tc9HzwTks5s6RkSgaJpZM4Oxe2h .
@dfruddffdc nice work! 👍
David -- I read the Readme in your ApexMocks project https://github.com/dfruddffdc/apex-mocks-sfdx and it looks excellent. I even learned something about fflib_System which was new to me.
Some suggested additional doc that would help new users:
- How to mock selectors that return parents and children. I use GitHub SObjectFabricator https://github.com/mattaddy/SObjectFabricator for this as I find it has better expressive power and much easier to read than fflib_ApexMocksUtil.makeRelationship(). Both approaches use the Json serialize/deserialize trick. Selector mocking allows return of formula fields and system audit fields - a common testing challenge.
- How and why one uses ArgumentCaptors
- An example using the sObjectWith matcher (or link to the andyinthecloud blog post on this)
- Troubleshooting why your mocks.verify fail (for me - it is typically because the matcher is wrong but typical reasons for them being wrong)
- Using matchers when the mocked method has multiple arguments
- A practical, non-contrived, example of combined matchers (I haven't come up with one yet in the context of services - domains - selectors)
The unit test vs system test paragraph at the end is an interesting one and can be further motivated with:
- Examples most of us can relate to -- viz. the burden of setting up w/ DML Accounts - Contacts - Opportunities - Pricebooks - Pricebook Entries - Product2 - OpportunityLineItem all to test combinations of a handful of fields to see if the database is updated as per expectation. The Selector and UnitOfWork pattern fit in nicely here.
- The expressive power of mocks.verify to ensure that some service is ONLY called in an after Update context, not in before/after insert or before update - the Domain layer is handy here
- The expressive power of mocks.verify to ensure your selectors and services are called only with the expected parameter values. Systems testing rarely tests to see if the code does more work than it is supposed to or that contracts between clients and services are adhered to - instead, systems testing tends to focus on the resultant database updates and not per se how those were attained
One must absolutely have systems tests for Selectors as if they are only mocked, you have no way of proving that the filter criteria was coded right or that the fields needed by the selector consumer are even fetched! One must have system tests for classes that update the database as mocking the Unit Of Work layer won't execute validation rules, fire downstream triggers, workflows, process builder flows, or exercise sharing rules
Lastly, I've also been making notes in my Packt Publishing "Mockito" book on which Mockito features (i.e. specific Matchers) are not implemented in ApexMocks but you might already have a good list and that would merit inclusion in the Readme.
Eric
On Sun, Nov 26, 2017 at 11:22 AM, Andrew Fawcett [email protected] wrote:
@dfruddffdc https://github.com/dfruddffdc nice work! 👍
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/47#issuecomment-347031597, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNrci-bcAzJAr1d7DrJIR8Vh89Ucdeoks5s6bpegaJpZM4Oxe2h .
@cropredy thats an excellent tip, we should for sure reference that on the readme as being complementary. I also found that i wanted to mock complex selector responses and thus the following method was born, https://github.com/financialforcedev/fflib-apex-mocks/blob/master/src/classes/fflib_ApexMocksUtils.cls#L67.
@cropredy Thanks for the detailed feedback, very useful!
While puzzling over why one of my mocks wasn't working I reread the Readme.md
last night and I had two additional thoughts:
- There is no link in the related refs to https://github.com/dfruddffdc/apex-mocks-sfdx which explains practical techniques for dependency injection
- AFAIK, there is no example in ApexMocks.Test that illustrates dependency injection for the fflib_MyList class that is used for all the testing.
Leading me to suggest that this section of the readme.md be enhanced as follows:
when() dependency stubbing
// given mocks
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_MyList.IList mockList = (fflib_MyList.IList)mocks.mock(fflib_MyList.class);
mocks.startStubbing();
mocks.when(mockList.get(0)).thenReturn('bob');
mocks.when(mockList.get(1)).thenReturn('fred');
mocks.stopStubbing();
// given mocks injected (see also https://github.com/dfruddffdc/apex-mocks-sfdx)
Bar b = new Bar(mockList);
// when Bar invoked
String result = b.doStuff();
// then verify
System.assertEquals(result, 'bob,fred','doStuff assembles a comma-separated list');
where the object under test is:
public class Bar
private fflib_MyList.IList myList; // interface so real and mock can be used
public Bar() {this.myList = new fflib_MyList.IList();} // normal PROD
public Bar(fflib_MyList.IList mockMyList) {this.myList = mockMyList;} // for unit testing
String public doStuff() {
String[] resultVals = new List<String>();
for (Integer i=0; i < 2; i++) {
myList.add(i);
resultVals.add(myList.get(i);
}
return String.join(resultVals,',');
}
So - why did I add this bit?
- We use fflib patterns throughout our org; we use mock selectors, services, domains, and unitOfWork all the time. Those mocks get injected via (using selector as an example)
Application.Selector.setMock(mocksSelector);
and when the code executes axxxSelector.newInstance().selectByXXX(..)
, the fflib_Application class returns the mocked selector via a factory rather than the PROD selector. All very neat - and somewhat magical to me and my team members who were introduced to fflib patterns. - But after living under the spell of this magic for months, we wanted to extend the use of apexmocks to classes that weren't services/domains/selectors. But that means you need to have a way to inject the mocks to the code under test. And none of the examples in this readme, fflib_ApexMocksTest or the related references show how to do this (at least for the StubApi implementation of apexMocks).
- Of course, when writing this comment, I have now all figured this out but as this readme was my first port of call I thought this could help others.