DeloitteEngineering Blog | Netherlands

Extensive test/mocking example guide in fflib

Cover

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 scan/adopt when a new joiner joins your project. It’s not coincidental that most coding languages have 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.

While in our previous (Apex Enterprise Patterns) my colleague Adam focused more on the benefits and definition of the fflib framework; 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 understood the layers, the benefits and how to apply (or copy-paste). However, when they were asked to build from scratch the doubt kicked in. Having an example snippet to refer to as back-up can help a lot. In addition, I believe it’s only beneficial when the core and reasoning is really explained so one can 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… Taking the time to go through it in once might be exhausting, so I’d suggest to simply bookmark this page and visit it when in need. Or simply checkout one class and how the testing and mocking is performed. See it as a reference which you can visit, whenever applicable. And do you have comments or feedback? Please feel free to reach out.

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/class takes ownership for all front-end communication. This particular class calls the selector method and transforms the response to something understandable by the front-end. Because of this, the selector method simply returns a List of records and thus remains caller-agnostic (allowing easier reuse for future reference); In addition, the calling 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.

SRV_Response is a class we consistently use at Deloitte to make sure all front-end components can expect the same structure of response and thus simplify debugging and supporting others on an unknown component. In the attempt to reduce the length of this blog post, I've chosen to leave this class out. Only relevance is knowing the class is used to set variables like isSuccess, payload. If you'd like to know more specifically on this class, feel free to reach out.

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( true, true, 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:

1. Boilerplate

2. Default fields to query

3. Effective selector methods

selectActiveByTypeAndUsers(), the first ‘custom code’ in this class and where it gets interesting:

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.

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.

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 ability (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:

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:

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.

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:

  1. Instantiation of a ‘hollow’ class, where each method either return null or does nothing;
  2. Definition/redefining the response for specific methods to act in a certain way within this test method;
  3. 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:

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:

⚠️ 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.

  1. If the tested-method returns an output, one can assign it to a variable in the test method and assert the correctness;
  2. If there is no output, but one of the input-parameters is updated or a class variable is set (when either public or @TestVisible), 3. these could be easily asserted as well;
  3. When no variable is returned or set, we can verify whether at least another class’ method is called (with exactly the reference parameters requested);
  4. 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.

  1. mocks.verify( srvResponse, 1 ) – each mocked/hollow class contains a verifying-Boolean. This Boolean determines the ‘behaviour’ of the mocked class. Instead of returning what we defined (false), or simply return null when that method is called, it will perform a verification when set to true. Most likely you can guess the single-responsibility of the verify method right? ☺. Next to toggling the Boolean, is also passes the verificationMode, 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;
  2. ( ( SRV_Response ) {1} ) – the verify 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 reusable Object Type (instead of the named SRV_Response). Because of this, one first needs to cast Object back to SRV_Respone, before calling a method from SRV_Response;
  3. So far we’ve set srvResponse.Verifying = true and specified the requested verificationMode. 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 use fflib_Match to describe our match. After the method is being called and the verification passes, the Verifying flag is reset to false. 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

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:

⚠️ 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:

  1. mock() – this converts all Builders to effective Salesforce records
  2. mockWithIds() – next to converting, this method also ensures each Builder and all child Builders have a valid Salesforce Id populated, 3. without inserting the record in the Database. Note, parent builders will remain without Ids
  3. save() – converts all Builders to Salesforce records, but also inserts them to the Database in case really needed
  4. 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!