When writing code, one could either simply get the job done, or one could look ahead and prepare code which is easier to maintain and to scan/adopt when a new joiner joins your project. It’s not coincidental that most coding languages know frameworks to structure code like Model-View-Controller, Object-Oriented-Programming etc. They all share the intent to organise code, give each piece/method it’s own purpose, it’s logical content, it’s own single-responsibility. By doing so every future developer – or the client you might hand it over to – is able to easily find which piece of code might require extension or accommodates the cause of a bug.
Personally, fflib (Financialforce library) has become my holy grail and true passion. This library applies the Apex Enterprise Design Pattern (recommended by Salesforce in this Trail), which – underneath – very delicately provides a Pattern to structure code, giving each method it’s own single responsibility and ensure your code is well-organised allowing anyone else to easily find what’s needed.
Where my previous post (Enterprise Design Pattern (fflib)) focused more on the benefits and definition, in this post I’d like to share an extensive snippet, with the focus on the usage and delicacy of mocking in test classes. Quite often I receive questions from those who perfectly understand the layers, their benefits and how to apply them (or copy-paste). However, when being asked to build from scratch, the doubt kicks in. Having an example snippet to refer to as back-up can help a lot. In addition, I believe it’s only beneficial when really being explained/understand what’s going on and therefore I’ll try to elaborate as much as possible on the how and why.
How to read this post:
When posting this article, the length grew massively over time. Taking the time to go through it in once might be exhausting, so I’d suggest to simply bookmark it and visit it when you need it. Grab a class for testing and see the details and elaborations. See it as a reference which you can visit, whenever applicable. And do you have comments or feedback? Please leave a comment or contact me directly.
Mocking allows to ‘reprogram’ a full class. Doing so one can ‘fake’ different behaviour during test runs compared to the real/original implementation. While this might seem odd, this allows to solely test the behaviour of that method; the single and only responsibility. Imagine each method testing it’s own functionality, this allows to simplify test methods and not duplicate data preparation or cover the same code over and over again (like a Account Trigger Handler).
Example: LWC Controller to retrieve CampaignMembers
Scenario: Within a Community one wants to show the list of active CampaignMembers the end user is subscribed to. Hence, a Lightning Web Component (LWC) is created which calls an Apex Controller class. In turn this calls the CampaignMember Selector class to receive the CampaignMembers for the running user and return that in a standardised way to the front-end.
Ready? Let’s do this!
The code
CTRL_CampaignMembers
public with sharing class CTRL_CampaignMembers{ /** * Method to return all CampaignMembers for the running user, given a Campaign Type * * @param type Required input specifying the Type of Campaign the CampaignMembers should relate to * @return List of CampaignMembers related to the current user and type */ @AuraEnabled public static SRV_Response.LWC_Response getAllMineByType( String type ){ try{ fflib.verifyNotBlank( type, 'type' ); // Query for active CampaignMember given the type and user. List<CampaignMember> campaignMembersList = SEL_CampaignMembers.newInstance().selectActiveByTypeAndUsers( type, new Set<Id>{ UserInfo.getUserId() } ); return ( SRV_Response.LWC_Response ) SRV_Response.newInstance() .setResponse( SRV_Response.TYPES.OK, null ) .setPayload( campaignMembersList ) .getResponse(); } catch( Exception ex ){ return ( SRV_Response.LWC_Response ) SRV_Response.newInstance() .setResponse( SRV_Response.TYPES.PROCESSING_ERROR, ex.getMessage() ) .getResponse(); } } }
The Controller layer/classes take ownership for all front-end communication. They call the caller-agnostic methods and transform the response to something understandable in the front-end. Thereby, the Selector simply returns a List of records, so it can also be used by another future method; but the Controller transforms it to a wrapper, or a standardised response. In addition, the Community User only requires access to this Controller class and not to the Selector class, which would grant the user access to all methods in there, even those which are not relevant. By structuring our code, we apply a slightly stricter security of code availability.
In this scenario the layers are prefixed with specific values (CTRL, DOM, SEL, SRV). However, please note, each project might define a different naming convention. Only consistency is important to apply.
In addition, it might be worth to note the following things:
- The try-catch statement allows to structure all Exceptions in one way, e.g. both missing input parameter AND query issues;
- A generic reusable input-verification method is called to allow checking whether the input is correctly provided;
- A selector method is called to perform the effective query;
- A service class (
SRV_Response
) is called, to allow one standardised way of what a LWC method receives. Else each LWC would potentially require different response handling making it harder to maintain and train new-joiners.
SEL_CampaignMembers
public with sharing class SEL_CampaignMembers extends fflib_SObjectSelector{ /** * Creates a new instance of the selector via the application class. This is here to allow unit tests to override * and inject a mock instead of this class or to switch out this class for a new version. */ public static SEL_CampaignMembers newInstance(){ return ( SEL_CampaignMembers ) fflib.selector.newInstance( CampaignMember.SObjectType ); } /** * Returns the SObject type for the selector. This is used to retrieve the sObject name when building the SOQL * queries. */ public Schema.SObjectType getSObjectType(){ return CampaignMember.SObjectType; } /** * Returns the SObject fields which should be selected within all queries (unless not required). For selecting * fields through relationships @see{Set<String> getRelatedFieldSet()}. */ public List<Schema.SObjectField> getSObjectFieldList(){ return new List<Schema.SObjectField>{ CampaignMember.Id, CampaignMember.CampaignId, CampaignMember.ContactId }; } /** * Returns the Relationship fields (thus fields from parent/lookup records) to allow easy inclusion. * Especially useful for junction records. Can be called via fourth parameter in newQueryFactory(). */ public override Set<String> getRelatedFieldSet(){ return new Set<String>{ 'Campaign.Description', 'Contact.Email' }; } /** * @param type Campaign Type the CampaignMember should be related to * @param userIds Set of UserIds the CampaignMembers should be linked to (via Community Users) * @return List of CampaignMembers matching the requested Type for the given Users */ public List<CampaignMember> selectActiveByTypeAndUsers( String type, Set<Id> userIds ){ fflib_QueryFactory query = newQueryFactory( withSharing, withSharing, true, true ); List<String> conditions = new List<String>{ 'Campaign.Type = :type', 'Campaign.IsActive = true', 'ContactId IN ( SELECT ContactId FROM User WHERE Id IN :userIds )' }; query.setCondition( String.join( conditions, ' AND ') ); return ( List<CampaignMember> ) Database.query( query.toSOQL() ); } }
This selector class is rather standard, though there are of course quite some things to elaborate. From top to bottom:
- By default and best-practice is to have all selector classes
public with sharing
to allow safe reuse in the future; - In addition, this class extends
fflib_SObjectSelector
to automatically benefit from all out-of-the-box functionality the fflib framework overs regarding selectors. Like the next two methods:newInstance()
andgetSObjectType()
. These ‘boilerplate methods’ are required to have the fflib framework handle this correctly; - In the
newInstance()
method you see the defaultselector
variable being called, specifying the SObjectType. The code behind it will match it to the correct implementation and return this; getSObjectFieldList()
grants the ability to define a specific set of fields which should be included in ALL queries performed within this selector. This is especially handy on smaller SObjects, to prevent duplication of field references throughout the different selector methods. For larger SObjects it is good to align within your project on the best practice. When also developing for users with restricted permissions (like Community Users) this might cause issues when in the future someone extends the list of fields and your user doesn’t have access to that new field, causing the login to fail;getRelatedFieldSet()
is an optional method which can be overridden and enables similarly togetSObjectFieldList()
but then for relationship fields. Note, the first method specifies SObjectFields while the second provides Strings. This is because relationships could go up to 5-parent-levels and that can’t be managed via SObjectFields. Luckily the fflib-framework allows us to provide Strings which in the back then is converted to the corresponding SObjectFields;- In all my projects we’ve agreed to always order field references alphabetically so a next developer can easily check whether a field is already listed, especially when the lists become more lengthy;
selectActiveByTypeAndUsers
(), the first ‘custom code’ in this class and where it gets interesting:- Even while the current scenario doesn’t ask for it, you see the method is bulkified. It is prepared for future extensions where one might receive the CMs for multiple users/types. When not doing so, this would require future duplication or changes and with that require regression testing on existing functionality;
newQueryFactory()
, the magical method to kick of each query construction. This methods has up to four parameters:enforceCRUD
– whether or not BEFORE querying fflib should verify if the running user has Read rights on this object. CRUD stands for Create, Read, Update, Delete, aka object permissions;enforceFLS
– whether or not BEFORE querying fflib should verify if the running user has Field Level Security, aka permission to read the selected fields;includeSelectorFields
– whether or not the standard fields fromgetSObjectFieldList()
should be added in the query;includeRelatedFields
– whether or not the standard fields fromgetRelatedFieldSet()
should be added in the query.
- While you could also just specify
query.setCondition( 'Campaign.Type = :type AND Campaign.IsActive = true );
experience has learned the construction of Strings via a List can be very handy. Especially when methods are made more dynamic, e.g. when addingif( userIds.isEmpty ){ conditions.add( ContactId IN ... ); }
instead of adding it by default. Then this one method also becomes reusable for a developer who only wants to query all CampaignMembers for active Campaigns of a given type; - Following best-practices one always wants to use binded variables (
:type
) for Apex to run all query safeties for you and handle/prevent things like SOQL Injection; - Lastly, the query-string is retrieved and passed into
Database.query()
. At that moment, Salesforce will try to ‘replace’ the bind variables (:type
) with actual values. In case the variable doesn’t exist at that moment, aSystem.QueryException
is thrown.
Hopefully this provides an extensive explanation to really understand why we are writing this code so it can help you to adopt this and write your own beautiful selector methods! Most important of all, always keep reusability and Don’t Repeat Yourself in mind.
SRV_Response
For the sake of density, to focus on the right things and given each project often having their own implementation of a Response class, I won’t share the full class. However, this class applies the Builder-patterns aka Fluent Interfaces, which has an impact on how to test/mock this and therefore I thought it was noteworthy to elaborate a bit on this pattern. If you are familiar with the Builder pattern/method-chaining, you can skip this section.
To allow method chaining e.g. (srvResponse.setPayload().getResponse()
instead of srvResponse.setPayload(); srvResponse.getResponse()
) the method should simply return ‘itself’ aka this
. In fact, when running srvResponse.setPayload()
this returns srvResponse
and is therefore the same as referencing the variable.
public Response setPayload( Object payload ){ this.payload = payload; return this; }
Test classes
Test classes should NOT be solely written to match Salesforce’s requirement to have at least 75% code coverage. More importantly, it should be written to ensure all possible scenarios are properly handled in your code (like error handling, null-pointers etc). This makes your code more robust and guarantees any unintended future change to be quickly detected due to a build failure. Other colleagues can more safely re-use/extend your code, even while their scenario might be slightly different. In other words, it gives us some assurance of the code being of quality and behaves as designed.
Hammer tests Lastly, Salesforce can use a subset of clients test classes to verify the regression for new releases (called ‘Hammer tests‘). So the more strict your test code, the better Salesforce can keep the services consistent and working with retrospect :).
Performance
Given test classes are required to be ran while deploying to Production, the performance (aka total runtime) has a direct effect on how frequent we can deploy. Hence, we aim for efficient and performing classes.
Logically, we want all our test classes to specify isParallel = true
, implying that class can be ran simultaneously with other test classes, without impeding each other. One of the reasons while this might not be possible is when inserting test data, or modifying special objects like User, as the change from one class might impact the behaviour in the other.
In addition, the creation of test data often consumes most of the test-runtime window, due to the required database interactions, but also all triggers and automations to execute.
On that note, can you imagine/think how many of your classes currently insert an Account and thus, how much ‘duplicate testing’ happens on your Account Triggers? From the “single responsibility” and “don’t repeat yourself” principle this feels like a code-smell, or something we at least want to avoid if possible.
FFLIB to the testing rescue
In the testing domain the FFLIB framework opens up a full arsenal of functionality to very easily apply ‘mocking’; or in other words faking a response. For each method we can specify “Don’t run the real method, but just return …”. Remember all queries are moved to an isolated Selector method, which we can thus mock and return whatever local record we’ve drafted. This not only prevents the insertion, but also avoids the query to be effectively performed, which is often the most time-consuming – next to DML actions. Hence, this fully avoids the need to insert testdata and enables to set isParallel = true
. Can you already think of the test runtime impact?!
Next to mocking selectors, any other other method can be mocked, preventing the same scenario being covered multiple times and consume more time than needed. E.g. think of our Controller example. When the Selector test class already verifies whether selectActiveByTypeAndUsers()
works as expected, why should the Controller test class verify the same by inserting a CampaignMember and then check the response of the Selector method?
Thus, we design our test methods to only test that specific method, and mock test data to prevent inserts and allow isParallel
.
ℹ️ Performance: In Apex the ‘local changes’ are relatively low-cost in terms of performance. However, each transaction to the Database (insert, query etc) is quite costly. Eliminating those, is a massive impact to test runtime. In the past I’ve seen projects cutting down from 3h runtime to only 1h!
⚠️ Reduced testing responsibility: There are proponents and opponents to the more efficient and ‘component focused testing’. One disadvantage might be to lose context, and not being able to test validation rules etc. On the other hand this is a massive advantage for projects you might hand back to be maintained by the business. A change on a validation rule might cause all your previous test classes to fail. My perspective: as long as each method has it’s single-responsibility extensively tested, with multiple scenarios etc. then there should be no harm of not testing end-to-end. It’s however not a wildcard and it’s highly recommended to run automated testing tools etc. but in my perspective this is not the responsibility for Unit Testing. Do you feel differently? Please let reply in comment section!
Curious how that mocking works?! Let’s have a look!
SEL_CampaignMembers_TEST
Overall the Selector test method shouldn’t be too much of a surprise.
@IsTest( isParallel = true ) private with sharing class SEL_CampaignMembers_TEST{ @IsTest private static void selectActiveByTypeAndUsers(){ SEL_CampaignMembers.newInstance().selectActiveByTypeAndUsers( null, new Set<Id>() ); fflib_QueryFactory result = fflib_QueryFactory.lastQueryFactory; // Validate selected fields Set<String> selectedFields = result.getSelectedFields(); System.assert( selectedFields.contains( 'CampaignId' ), 'Expected standard field CampaignId from getSObjectFieldList() to be part of query' ); System.assert( selectedFields.contains( 'Campaign.Description' ), 'Expected relationship field Campaign.Description from getRelatedFieldSet() to be part of query' ); // Validate conditions String condition = result.getCondition(); System.assert( condition.contains( 'Campaign.Type = :type' ), 'Expected Brand condition on parent Campaign record' ); System.assert( condition.contains( 'Campaign.IsActive = true' ), 'Expected parent campaign to be required active' ); System.assert( condition.contains( 'ContactId IN ( SELECT ContactId FROM User WHERE Id IN :userIds )' ), 'Expected a subselect condition to allow getting CMs by UserId' ); } }
At the moment of writing unfortunately there is no gability (yet) to prevent the Database.query()
to be called. However, since there is no test data inserted and SeeAllData
is false by default, no data will be returned and the query is performed as quick as possible.
Couple things to remark on the Selector class:
- After calling the query, the
lastQueryFactory
is retrieved to be able to apply asserts on the constructed QueryFactory. - The first validation checks the selected fields – the “…” in
SELECT ... FROM Object
– which returns a Set of Strings - The second validation checks the conditions which were added.
- One could also choose to verify whether the condition exactly matches a full string, which is more sensitive for spacing typos and such
- Note a
contains
is by default case-sensitive
- Note, all assertions have an ‘error-message’, this is especially useful for
assert
(in contrast toassertEqual()
) as the failing message would only say something like “Assertion failed, expected true, actual false” and a line-number. While if one provides an error-message this message is also shown, indicating the developer what might have gone wrong.
CTRL_CampaignMembers_TEST
@IsTest( isParallel = true ) private with sharing class CTRL_CampaignMembers_TEST{ @IsTest private static void getAllMyCampaignMembers(){ // Create mocks fflib_ApexMocks mocks = new fflib_ApexMocks(); SEL_CampaignMembers selCampaignMembers = ( SEL_CampaignMembers ) mocks.mock( SEL_CampaignMembers.class ); SRV_Response srvResponse = ( SRV_Response ) mocks.mock( SRV_Response.class ); // Given - create data String campaignType = 'Webinar'; CampaignMember cm = ( CampaignMember ) UT_TestFactory.aCampaignMember() .forContact( UT_TestFactory.aContact().with( Contact.Email, 'dummy@fake.email.provider' ) ) .forCampaign( UT_TestFactory.aCampaign().withType( campaignType ) ) .mockWithIds(); List<CampaignMember> queryResponseList = new List<CampaignMember>{ cm }; // Set mocks mocks.startStubbing(); mocks.when( selCampaignMembers.sObjectType() ).thenReturn( CampaignMember.SObjectType ); mocks.when( selCampaignMembers.selectActiveByTypeAndUsers( fflib_Match.eqString( campaignType ), ( Set<Id> ) fflib_Match.anyObject() ) ).thenReturn( queryResponseList ); mocks.when( srvResponse.setResponse( SRV_Response.TYPES.OK, null ) ).thenReturn( srvResponse ); mocks.when( srvResponse.setPayload( queryResponseList ) ).thenReturn( srvResponse ); mocks.stopStubbing(); fflib.selector.setMock( selCampaignMembers ); fflib.service.setMock( SRV_Response.IService.class, srvResponse ); // When - perform test CTRL_CampaignMembers.getAllMineByType( campaignType ); // Then - validate // Since the SRV_Response is mocked, the payload is not actually set so we need to use ArgumentCaptor to fetch the payload set by the Controller class fflib_ArgumentCaptor payloadArgument = new fflib_ArgumentCaptor(); ( ( SRV_Response ) mocks.verify( srvResponse, 1 ) ).setPayload( ( List<CampaignMember> ) payloadArgument.capture() ); List<CampaignMember> campaignMembersList = ( List<CampaignMember> ) payloadArgument.getValue(); System.assertNotEquals( null, campaignMembersList, 'Expected payload to be set and initiated' ); System.assertEquals( 1, campaignMembersList.size(), 'Expected one CampaignMember to be returned' ); System.assertEquals( cm, campaignMembersList[ 0 ], 'Expected first returned CampaignMember to be the mocked CampaignMember' ); } @IsTest private static void getAllMyCampaignMembers_exception(){ // Create mocks fflib_ApexMocks mocks = new fflib_ApexMocks(); SEL_CampaignMembers selCampaignMembers = ( SEL_CampaignMembers ) mocks.mock( SEL_CampaignMembers.class ); SRV_Response srvResponse = ( SRV_Response ) mocks.mock( SRV_Response.class ); // Given - set data fflib.MissingDataException fakeException = new fflib.MissingDataException( 'Just some nice Exception to be thrown to test Exception behaviour' ); // Set mocks mocks.startStubbing(); mocks.when( selCampaignMembers.sObjectType() ).thenReturn( CampaignMember.SObjectType ); mocks.when( selCampaignMembers.selectActiveByTypeAndUsers( fflib_Match.anyString(), ( Set<Id> ) fflib_Match.anyObject(), fflib_Match.anyBoolean() ) ).thenThrow( fakeException ); mocks.when( srvResponse.setResponse( ( SRV_Response.TYPES ) fflib_Match.anyObject(), fflib_Match.anyString() ) ).thenReturn( srvResponse ); mocks.stopStubbing(); fflib.selector.setMock( selCampaignMembers ); fflib.service.setMock( SRV_Response.IService.class, srvResponse ); // When - perform test CTRL_CampaignMembers.getAllMineByType( 'Irrelevant Type value' ); // Then - validate that Exception is thrown ( ( SRV_Response ) mocks.verify( srvResponse, 1 ) ).setResponse( SRV_Response.TYPES.PROCESSING_ERROR, fakeException.getMessage() ); } }
This test class is of course a bit more exciting since more is happening. On the other hand “only two scenarios” are covered:
- A successful retrieval – is the queried CampaignMember correctly returned (to the front-end)
- An Exception in retrieval – is the Exception message passed and the response to the Lightning Web Component indeed stating the method failed?
Each testmethod shares the same structure (see comments), which helps to easily scan and understand the test methods. This is recommended, but not a must as this should be a project wide decision. Consistency is key.
Create mocks
– creation of ‘hollow’ classes. When a method is called of such mocked classnull
will be returned and nothing is performed (see more details below);Given - set data
– creation of all relevant test data for this scenario;Set mocks
– the magic! Here we ‘re-program’ what the hollow class should do when a specific class is called. This section ends with the assignment of the ‘redefined mocked class’ to be applied by the FFLIB framework during that test method run;When - perform test
– the effective call to the method being tested, passing the relevant input;Then - validate
– assertions to verify whether the method indeed did what it was designed to do; the stricter we make those, the more guarantee we gain
In the below chapters we’ll deep-dive in each step to allow full understanding.
Test data creation (1)
Since we don’t need to save the records to the database, we only need those ‘in memory’. We, thus, simply write Account acc = new Account()
, populate the scenario related fields and pass this acc
-variable into the method that should do something with it.
It is a best practice to always use references over hardcoded textual references. Like in the getAllMyCampaignMembers()
-test method, where Type
is defined in one place and then referenced in three places. This is simply to avoid a future change in one place to cause failures with the other places. Note, it is definitely not needed to construct a separate String
variable each time, as long as duplicate hard text is avoided (e.g. one could also specify the textual Type
directly in the Campaign construction and then reference cm.Campaign.Type
when calling the method. As long as one uses references instead of duplicating hardcoded text).
The next thing that might look odd is the UT_TestFactory
approach, this is a structure within the Deloitte framework which allows constructing record-instances easily. While new Account( Name = 'dummy' );
is perfect for most situations, there is a challenge when specifying Lookups, child relations and mocking IDs. Those constructs are all taken care of in the TestFactoryBuilders for you to avoid too much (duplicated) complexity (see more details in the last sections).
Mocking classes and methods (2)
As read above, mocking is in essence not much more than ‘faking a certain response’. While it might be perceived complex, it might help to simplify it to the following three steps:
- Instantiation of a ‘hollow’ class, where each method either
return null
or does nothing; - Definition/redefining the response for specific methods to act in a certain way within this test method;
- Assignment of this mocked and redefined class to overrule the standard implementation
In this way, the method being tested will not effectively perform the original business logic, but only what you specified.
Hollow/mocked class A rather abstract construct, which can be seen as making a clone from the real class, stripping all logic from the method-bodies, adding a return null;
for non-void methods and leaving the void-methods empty. Then, when having a ‘hollow’ class, with exactly the same method-signatures as the real class, one can reprogram/fill the method-bodies with the desired testing output. The original logic is then not performed, but only the reprogrammed return or logic is performed. Applying this class to the framework, having it being initiated instead of the ‘real class’ allows to prevent too much logic to be performed, or being performed duplicate in the same unit test runs.
In the above three steps, often the 2nd step is most challenging and the 3rd forgotten 😉 So let’s start with the last step!
Apply mocked class to overrule standard implementation
To make sure your mocked/overridden class instance is applied during fflib runtime, we need to inform the fflib factory:
fflib.selector.setMock( selAccounts );
fflib.domain.setMock( domAccounts );
fflib.service.setMock( SRV_Response.class, srvResponse );
fflib.unitOfWork.setMock( uowMock );
One might notice that for Services we need to specify the ‘Interface class’, to make sure fflib knows when to construct this class. Selectors and Domains are mapped based on SObjectType, but we don’t need to specify that into setMock()
; this is handled when defining the sObjectType()
-method. Thus, if you specified setMock()
but your selector doesn’t have an override for the sObjectType()
method (and thus returns the default: null
), fflib can’t match it and the standard Selector will be used in your tests…
Hence, ALWAYS check whether you’ve added the setMock()
and for Selectors and Domains overridden sObjectType()
.
ℹ️ Did you notice to similarities between fflib.selector.newInstance( CampaignMember.SObjectType );
in the Selector class and the fflib.selector.setMock()
to enforce the ‘hollow’/mocked class is applied in the framework? This is exactly what we’re doing! In the newInstance()
method a check is performed whether a mock exists and if so, that one is initiated, else the original full class is initiated. Pretty clever right?
Define/reprogram specific method responses
When reprogramming a method, it’s important to be aware of the distinction between void and non-void methods. Void methods don’t return anything, they simply perform logic. Calling such method in a mocked class simply does nothing. Often such methods are not reprogrammed, given no logic should be performed. Non-void methods on the other hand, do return a type, which is set to null
by default when not overruled in the mocked class.
Non-void methods (returning types)
The response of a non-void method in a hollow class can be defined using the structure of mocks.when().then()
. Here, when()
defines the method-signature – including the exact parameters -and then()
specifies what should be returned. Note, when any of the parameters programmed in the when()
only slightly mismatches from the effective method-call, there can’t be a match and the response will be the default of the mocked method: null
.
In addition to returning a type, one can also throw an Exception using mocks.when().thenThrow()
. This is extremely useful when testing failing DML transactions, or other Exceptions to be triggered!
Hopefully, you experience the then()
to be the easy part. This is where you provide whether the mocked method should return a List, null
or any specific value you define.
The when()
is often a bit more challenging. Especially since you can program different input parameters to return different values and thus your input-parameters should be very accurate to avoid a mismatch and null
to be returned; which often causes null-pointers
for selectors in code.
List<Account> accList = new List<Account>(); mocks.startStubbing(); mocks.when( selAccounts.sObjectType() ).thenReturn( Account.SObjectType ); mocks.when( selAccounts.getAllWithSharing( false ) ).thenReturn( accList ); mocks.when( selAccounts.getAllWithSharing( true ) ).thenReturn( null ); mocks.stopStubbing(); fflib.selector.setMock( selAccounts );
So let’s find out how we can specify the input parameters and what flexibility we have:
- Providing references
- The easiest approach is to pass in the exact reference of what will be provided. E.g. when the value which is returned from a Selector is directly the input of a Domain. In the test class you can then control both the output of the selector (
then()
) as the input of the domain method (when()
); - Or when a parameter from the tested method is directly passed into another layer method within the tested method (thus what you pass into the method in the
// When - perform test
section); - When providing a variable in the
when()
you should be 100% sure this is an identical reference. For Lists and Strings there is some flexibility, but forSet
or a Wrapper-instance the cache-reference should be identical. Thus, if the Set is constructed in the method being tested, you’ll never be able to override that behaviour specifying a reference. This has to do with how Sets work within Apex, but let’s skip that for now. - An example is shown above on the
setResponse()
andsetPayload()
- The easiest approach is to pass in the exact reference of what will be provided. E.g. when the value which is returned from a Selector is directly the input of a Domain. In the test class you can then control both the output of the selector (
- Providing a wildcard (
fflib_Match.anyObject()
)- When not knowing exactly what will be passed in, or when you want to ensure all method calls will return the
then()
-value, you can leveragefflib_Match
. This powerful class compares the parameters passed into the method during test runtime with the specified matcher; - But be aware, the
anyObject()
returns an Object, while your method might expect a String. To avoid compilation Type-errors, one should cast this method( String ) fflib_Match.anyObject()
. Luckily, there are some predefined matchers to prevent such lengthy and cluttered character-string, likeanyString()
. Those don’t require casting;
- When not knowing exactly what will be passed in, or when you want to ensure all method calls will return the
- Providing a comparable instance (
fflib_Match.eqBoolean( false )
)- As you might guess by now, the more strict you write your code, the better. But sometimes Sometimes you need to match one of the attributes, but would like to have the other attributes still using a reference/more strict. As can be read in the highlight below, we can’t mix references and matchers, but also don’t want to loosen the checks on the reference-parameters. For this,
fflib_Match
provideseq
-methods. This allows to specify a strict testing scenario, while allowing to mix with other matchers-parameters.
- As you might guess by now, the more strict you write your code, the better. But sometimes Sometimes you need to match one of the attributes, but would like to have the other attributes still using a reference/more strict. As can be read in the highlight below, we can’t mix references and matchers, but also don’t want to loosen the checks on the reference-parameters. For this,
⚠️ For the mocking functionality to work, either all input should be provided as fflib_Match, or none. When mixing those, your test method will fail with something like:
“fflib_ApexMocks.ApexMocksException: The number of matchers defined (2) does not match the number expected (3). If you are using matchers all arguments must be passed in as matchers.”
⚠️ A common mismatch occurs when a Matcher is set, like fflib_Match.anyString()
while in code tested null
is provided. This should be ‘matched’ by using fflib_Match.isNull()
.
An example of all three can be found in the above Controller test methods. Where the most ‘summarizing’ might be the selector mock:
mocks.when( selCampaignMembers.selectActiveByTypeAndUsers( fflib_Match.eqString( campaignType ), ( Set<Id> ) fflib_Match.anyObject() ) ).thenReturn( queryResponseList );
In the above example, the campaignType
input is known and preferred to be strict. However, the Set of Ids is constructed in the Controller and thus cannot be referenced. This, thus, requires to mix both fflib_Match
and reference-parameters. This way one prevents accidental mocking of an unexpected scenario and not detecting the code to work differently than designed.
Take a look at the structure for SRV_Response
. If we wouldn’t mock the response of each method to return the instance of that class, it would return null
. This would cause a null
-pointer on the next chained method ( null.setPayload()
). It’s, thus, important to remember that fluent-interfaces/Builder patterns always require some additional attention when mocking.
Void methods
Methods which don’t return anything, like uow.commitWork()
should be approached differently. In general, these methods are often not mocked, as they don’t return anything, and the logic is simply skipped. However, there are some use-cases to mock void methods, a.o. to throw an Exception. Especially for the unit-of-work this is extremely handy to ensure testing the try-catch statements.
String exceptionMessage = 'This is a mocked Exception message'; ( ( fflib_SObjectMocks.SObjectUnitOfWork ) mocks.doThrowWhen( new DMLException( exceptionMessage ), uowMock ) ).commitWork();
Do note the exceptionMessage
is defined as String variable – instead of a ‘textual error message’ – which allows referencing when validating. This is further elaborated in the next ‘Validation’ chapter.
Verification (3)
Up to now, we’ve created some local data/records and mocked the methods from other classes to avoid their’ logic to be tested multiple times, and return our fake data. The method in question did run, and all we have left is to verify whether the business logic of the tested method was effectively performed. E.g. where fields correctly populated, objects constructed, records registered to the UnitOfWork, and was commitWork()
called?
Also for the verification there are multiple options, depending on the scenario. However, in contrast to the mocking, multiple options can be combined to make the validation more strict and robust.
- If the tested-method returns an output, one can assign it to a variable in the test method and assert the correctness;
- If there is no output, but one of the input-parameters is updated or a class variable is set (when either public or @TestVisible), these could be easily asserted as well;
- When no variable is returned or set, we can verify whether at least another class’ method is called (with exactly the reference parameters requested);
- or, as cherry on top, fflib allows to capture the input parameters of other classes’ method, on which then asserts can be performed
The first two options are quite alike and used in SEL_CampaignMembers_TEST
. The Selector method does return a value, but since there is no test data setup, this would always return an empty List and give no guarantee the query is properly performed. Therefore, a class variable (lastQueryFactory
) is introduced which allows to Assert the constructed QueryFactory which in the end is converted to the query.
⚠️ Always remain conscious whether the returned value of a method is indeed relevant for assert. In the example of CTRL_CampaignMembers_TEST
the returned Response is simply set in the mocking and therefore be completely useless to assert. When asserting, you’d only be testing whether the fflib mocking framework behaves as expected. Remember, mocked methods simply return what you define, and none of the original logic is performed. Thus, the Response returned does not have a Payload nor message set, that logic should be tested as that logic is skipped/mocked and we didn’t set it in the mocked data.
Also the third and fourth option are comparable in terms of syntax used and might be rather hard to read for the first time, so let’s crack the code and get this baby rolling!
In CTRL_CampaignMembers_TEST
both options are shown in the different test methods, let’s start with method-call-verification and then expand by adding the input-parameter-capture.
( ( SRV_Response ) mocks.verify( srvResponse, 1 ) ).setResponse( SRV_Response.TYPES.PROCESSING_ERROR, fakeException.getMessage() );
This might look very complex/scary, but when understanding how Apex parses it, hopefully it becomes more clear.
mocks.verify( srvResponse, 1 )
– each mocked/hollow class contains averifying
-Boolean. This Boolean determines the ‘behaviour’ of the mocked class. Instead of returning what we defined (false
), or simply returnnull
when that method is called, it will perform a verification when set totrue
. Most likely you can guess the single-responsibility of theverify
method right? ☺. Next to toggling the Boolean, is also passes theverificationMode
, in this specific scenario an Integer which defines ‘should be called exactly N times’. With that the SRV_Response-class-mock is programmed to register the verification mode for the next method being called;( ( SRV_Response ) {1} )
– theverify
method returns the mocked instance, to allow chaining, like in the TestFactoryBuilders. Of course, the method should be reusable and can’t return each type of class and thus returns a generic and reusableObject
Type (instead of the namedSRV_Response
). Because of this, one first needs to castObject
back toSRV_Respone
, before calling a method fromSRV_Response
;- So far we’ve set
srvResponse.Verifying = true
and specified the requestedverificationMode
. That’s it. The next step is to specify which exact method-signature should be called one time. For this we use the same structure as for mocking: either we specify the exact variable, like the above example, or we usefflib_Match
to describe our match. After the method is being called and the verification passes, theVerifying
flag is reset tofalse
. In case of a failed verification, thus when that method is not called exactly the N times, an assertion will fail.
To summarise, we’ve updated the behaviour of our mocked class and then asked it to ‘verify whether that method was called’.
That wasn’t too bad after all right? 🙂
// Since SRV_Response is mocked, the payload can't be verified by the returned Response. // Hence, ArgumentCaptor should be used to fetch the payload which was passed in as input-parameter to the mocked SRV_Response class, provided/constructed by the Controller class fflib_ArgumentCaptor payloadArgument = new fflib_ArgumentCaptor(); ( ( SRV_Response ) mocks.verify( srvResponse, 1 ) ).setPayload( ( List<CampaignMember> ) payloadArgument.capture() ); List<CampaignMember> campaignMembersList = ( List<CampaignMember> ) payloadArgument.getValue();
Okay, let’s continue to the fourth and last option. Where we have three input-parameter options for mocking (reference, fflib_Match.any...
and fflib_Match.eq...
), there is one additional one for verification: fflib_ArgumentCaptor
.
This class allows to capture/store any input parameter passed into a method, and should only be used when verifying. From a matching perspective, this is comparable to the fflib_Match.anyObject()
, only allowing you to capture the input.
While most often one will use getValue()
to get the input record, it might be that the method is called multiple times, e.g. when specifying registerDirty()
which is once called for Account and once called for Contact. Then one uses getValues()
to receive all records registered, in the chronological order.
ℹ️ After dissecting the above verification method signature, hopefully the void-Exception mocking structure also becomes more readable. Also there, the first part sets a Boolean-flag, in this case not Verifying (after test-run) but DoThrowWhenExceptions, which causes the first next referenced method on the mocked class to be ‘programmed’ to throw the specified Exception. Then when the method to be tested runs and calls that explicit method, the Exception is thrown. ( ( fflib_SObjectMocks.SObjectUnitOfWork ) mocks.doThrowWhen( new DMLException( exceptionMessage ), uowMock ) ).commitWork();
Test Data Factory Builders
Hopefully, you’ve noticed the fflib framework aims to avoid database interactions to ensure fast and performing test methods, which have the sole responsibility of testing those methods’ single-responsibilities. Nevertheless, all test methods require proper data and data construction on its own can be quite lengthy and cause a lot of duplicated code. Because of that one enthusiastic Deloitte colleague started the TestFactory and TestFactoryBuilder. There are several comparable comparable frameworks online, though this felt most logical to share for a full and proper documentation.
TestFactory
The UT_TestFactory
is just enabling a short reference to the underlying TestFactoryBuilders. In addition, it provides an overview of all available Builders ánd does allow ‘short-references’ for more advanced objects. Example for a Junction record like AccountContactRole, which 9 out of 10 times will require both an Account and Contact populated could simply be initiated via: UT_TestFactory.anAccountContactRole( accBuilder, conBuilder );
instead of UT_TestFactory.anAccountContactRole().forAccount( accBuilder ).forContact( conBuilder );
. This is of course a simple example on how to prevent duplicate code across your test methods and centralise it.
@IsTest public class UT_TestFactory{ public static UT_TestFactoryBuilders.CampaignBuilder aCampaign(){ return new UT_TestFactoryBuilders.CampaignBuilder(); } public static UT_TestFactoryBuilders.CampaignMemberBuilder aCampaignMember(){ return new UT_TestFactoryBuilders.CampaignMemberBuilder(); } }
TestFactoryBuilders
The UT_TestFactoryBuilders
class is where the real records are being constructed. In the back-end the values are set in a JSON structure after which they are (de)serialized to the requested Object. The below example shows two example Builder classes. The first constructs a Campaign, the second a CampaignMember. This allows to demo the ability to register both a parent and child relation, and ensure the output is likewise to how it would be retrieved via a query using a parent-lookup reference or a subselect.
/** * Test Factory Builders - to allow easy construction of mock data * Each Builder method should satisfy the following naming conventions, and ordered in below sequence: * - as scenario function to populate multiple fields * - for population of lookup fields (to parent) * - add population of child relations (to add childs) * - is population of boolean fields * - with population of any other fields */ @IsTest public class UT_TestFactoryBuilders{ // ... public class CampaignBuilder extends BuilderBase{ public CampaignBuilder(){ super( Campaign.SObjectType ); } public CampaignBuilder addMember( CampaignMemberBuilder builder ){ addChild( CampaignMember.CampaignId, builder ); return this; } public CampaignBuilder withType( String type ){ set( Campaign.Type, type ); return this; } public CampaignBuilder with( SObjectField field, String value ){ set( field, value ); return this; } } public class CampaignMemberBuilder extends BuilderBase{ public CampaignMemberBuilder(){ super( CampaignMember.SObjectType ); } public CampaignMemberBuilder forCampaign( CampaignBuilder builder ){ setParent( CampaignMember.CampaignId, builder ); return this; } public CampaignMemberBuilder forContact( ContactBuilder builder ){ setParent( CampaignMember.ContactId, builder ); return this; } public CampaignMemberBuilder with( SObjectField fieldName, String value ){ set( fieldName, value ); return this; } } }
Each Builder starts with a constructor
, which will ensure the standard methods like save()
, mock()
and mockWithIds()
to be made available. All methods created will returns “itself” (its’ instance) as previously shared. This allows chaining of methods in the ‘Builder pattern’/’Fluent interface’ as is explained for the SRV_Response class above.
Then, each Builder have can its own methods. For this it is crucial to have a clear alignment within your project on naming conventions and it’s highly recommended to agree on a sequence to allow easy reuse/search and prevent accidental duplicate methods when Builders grow large. In the example above, the sequence is defined in the class-header stating:
as
– used to define a scenario e.g.asIndividual
for an AccountBuilder, which will set all default values for an Individual Account;for
– used to populate a lookup towards a parent. This sets both
andCampaignMember.Campaign
CampaignMember.CampaignId
and thus perfectly mimics how the record would be populated when retrieved from SOQL;add
– used to populate child records. This sets e.g.Account.Cases
and thus perfctly mimics how the record would be returned when retrieving a subselect, likeSELECT Name, ( SELECT Id FROM CampaignMembers ) FROM Campaign
.is
– used to populate Boolean values, likeisActive()
with
– used to populate both specific fields (e.g.withType
) or allow any field to be specified.- It’s recommended, though a project-specific standard, whether to create
with
methods for each attribute to set, or to allow genericwith()
-methods to prevent extensive coding. In our projects this often depends on the (expected) frequency of usage. - In case of allowing a generic
with()
method, it would be best to suggest referencing an SObjectField instead of a String representation of the field.Then, when an API name of a field is changed, Apex can detect your test class is effectively populating that field, while if it is a String, it wouldn’t know. - Lastly, in the above example the generic
with()
method is set to receive a String as input, though when requiring a Date, DateTime, or any other field, this can of course perfectly be created aswith( SObjectField field, DateTime dt )
or construct a specificwithAssignmentDate( DateTime dt )
for it.
- It’s recommended, though a project-specific standard, whether to create
⚠️ It’s important to determine whether you need a Lookup or a Child, as when setting forAccount
, the child relation is not set; and when specifying addCases
, the child Cases will not have the Case.AccountId
specified.
While the above was set to define the population of the record, there is of course the end goal to receive a (local) record of it to apply in the test method. For this there are 4 options:
mock()
– this converts all Builders to effective Salesforce recordsmockWithIds()
– next to converting, this method also ensures each Builder and all child Builders have a valid Salesforce Id populated, without inserting the record in the Database. Note, parent builders will remain without Idssave()
– converts all Builders to Salesforce records, but also inserts them to the Database in case really needed- Sometimes there is no need to fetch the record from it, but the
UT_TestFactoryBuilder
can simply be passed into another Builder.
It is up to you as Developer whether you already want an Id (e.g. in an Update TriggerHandler), or explicitly require to Id to be set (e.g. to avoid the unit of work registerNew()
to throw an Exception).
Rule of thumb for TestFactoryBuilders, is to always encourage reusability and reduce the code across the codebase, while not introducing a method just to be referenced once.
Wrap-up
Like shared at the start, this blogpost can be better seen as an extensive guide with several examples on how to construct and apply proper test logic within the fflib (apex-common) framework. The sole intention is to have efficient and clean code, which is easy to maintain and quick to deploy. With this duplicate code and operations are avoided as much as possible and database connections are reduced as much as possible.
While I don’t expect you to read this before going to sleep or in one breath, I hope this might help as a reference for future test classes or for juniors to get a grasp on the wonders of the fflib testing framework. Hope this wasn’t too lengthy (like the opposite of how code should be ☺) and I’d love to hear your thoughts, feedback and test method war stories. Please reach out and share!
5 thoughts to “Extensive test/mocking example guide in fflib”
Hi Reinier,
Great, helpful article! Thanks for taking the time to write it!
However, I wanted to ask where is this `fflib_QueryFactory.lastQueryFactory` coming from? I’ve researched the entire fflib repo and couldn’t find the ‘lastQueryFactory’ in any of the previous code.
Truth is, for my testing would help me a lot so I would really want to know where you got that.
Thanks,
Cristian
Hi Cristian, Ah very valid question, I missed this deviation before. Within Deloitte we’ve made quite some changes to the framework to simplify usage and improve testability. This extension involves
private static fflib_QueryFactory lastQueryFactory;
in thefflib_QueryFactory
class, and population at the end of thetoSOQL()
method, statinglastQueryFactory = this;
just before the return-statement. This allows much stricter test methods, verifying in fact whether the selector did what was expected from it. Happy coding!Have you every come across having to test a Service method that has inside a call to a private helper method? I was able to cover the private method just fine but when trying to test the service method that was calling that, I kept getting errors and can’t figure out a way to actually mock that because of it being private. I guess, the only way to move forward is to make that method public, add it to the service interface and mock it the same way we do everything else. But private or even protected have their use and we shouldn’t have to adjust this only because it cannot be mocked. However, maybe I’m wrong and if you know how to test that, please do share. Thanks!
Hi Cristian, thanks for reaching out and interesting that you received some errors on the private method. My thoughts would be:
– When mocking a class, all non-voids return null and voids do nothing, that implies that when ‘mocking the Service class’ there should be no call towards that private method; and it only performs what you’ve (opt.) stubbed
– When calling the Service class in a test method for coverage, this should behave the same as when calling it from running code outside test scope; implying that the error might have nothing to do with mocking/fflib etc, but more the implementation itself.
Based on your comment, the above things seem relevant, but honestly both feel illogical to apply, so could you maybe share your snippet over email, and the error, as I’ve the feeling that I’m not fully understanding the context and error sufficiently. Cheers!
Very interesting info!Perfect just what I was looking for!