If you need to create some custom functionality that is accessible to your users via a list view (or potentially a related list), then a list view button is the way to go. The reason that you should prefer to utilize an LWC (despite the fact there is no obvious way to use an LWC for a list view button) is because LWC’s load faster than any other component type and, of the available list view button options, they are the most consistent with Salesforce’s Lightning Experience look and feel.
Unfortunately, while it’s super simple to setup and LWC for a list view button, Salesforce has no documentation on how to do so and virtually no answers exist for how to do this anywhere online, but NO MORE!! Today I’ll show you three different methods of creating an LWC List View button! Two methods do not allow you to send the ids of selected records in a list view and one does. I’ll give you the pros and cons of each and how to setup each one below.
One additional note, all of the below solutions will allow the user to traverse from the list view button back to the exact list view they were previously on without the help of visualforce at all! Something else that was undocumented and challenging to figure out.
DISCLAIMER: As of this writing, LWC quick actions are not usable on list views, this could change in the future however, so make sure to investigate that.
Setting up the Lightning Web Component
In any of the three scenarios we are going to use the same Lightning Web Component to demo the functionality, although in the third scenario (a flow based scenario) we will be making slight modifications to allow for the ids of records to get passed through to the component. Let’s take a look at the core of the component below (or on GitHub here).
HTML File:
<template>
<lightning-button label="Return to List View" onclick={close}></lightning-button>
</template>
Alright alright alright, so we have all of the files outlined above, let’s make talk about the close function in the JavaScript file first. I did an ENORMOUS amount of experimentation trying to get a list view button that spawns an LWC to allow me to traverse back to the exact list view that I had previously come from and this was the only way I could consistently get that to happen. I tried navigationmixin and it had trouble remembering where to go no matter what I did, I tried return urls, I tried a ton of junk, but that close function above does the trick every single time (at least in a browser, never tried on a mobile app). It always goes back to the exact list view I was on when I clicked the button.
So what does that function actually do? It basically looks to your browser windows history and moves back one page in time, the set timeout function allows it to take up to one second to make that happen. I would suggest using the set timeout as I had issues using this function without it, occasionally it wouldn’t have enough time to operate and failed. This however is used by thousands of users in my org now with no complaints.
The second thing I wanna go over is the XML File’s targets. For simplicity I just put every target we are going to need when we go through each of the three LWC list view button methods, that being said, you only need to declare the target for the method you choose to use. If you use the App Builder method, use the AppPage target, if you use the Tab method, use the Tab target, etc.
LWC List View Button Method 1: The Lightning App Builder Page Method
WHETHER YOU USE THIS METHOD OR NOT, READ THROUGH IT!! THE VAST MAJORITY OF THE STEPS ARE IDENTICAL FOR ALL OTHER VERSIONS SO THEY WILL ONLY BE SHOWN HERE AND REFERENCED IN THE OTHER SECTIONS!! Admittedly, this is my least favorite method, however it does work and some people may prefer it, so I’m gonna show you how to do it real quick. To use this method we need to create a lightning app builder app page. If you don’t know what that is, check out this trailhead. Once you’ve created the app builder page, you need to drag and drop the LWC (shown in the section above) onto the page. You can find the LWC in the section show below:
After you’ve placed your LWC on the page, save and activate your App Builder page. Then click anywhere in your lightning app page canvas that isn’t specifically your LWC to bring up the page information on the right side of the screen. Grab the “Developer Name” value, you need it for the next step.
Now that an app builder page houses our component, and we have the dev name for the app page, we need to setup a list view button to pop open our page for us. Kewlio Julio, let’s get to it.
Go to the object manager and find the object you are creating a list view button for. On the object page, click the “buttons links and actions” link, then click the “New Button or Link” on the top right of that page.
On the new button or link page, you are gonna fill out the follow: 1) Fill out a Label (this can be whatever you want) 2) Select the “List Button” display type 3) Select the “Display in existing window without sidebar or header” Behavior. 4) Select the “URL” Content Source 5) In the free text area put the following URL: /lightning/n/App_Page_Developer_Name 6) Save your button
To place your new LWC list view button on your objects list view, click on the “Search Layouts for Salesforce Classic” tab on your object and then click the drop down arrow next to the “List View” layout and select the “Edit” value in the drop down.
On the edit page for the list view layout, scroll down to the “Custom Buttons” section and select your new list view button.
Now, if you traverse to your object in the app launcher, you should be able to see your button on any list view and click it. This should result in your LWC popping up for you as shown below!
The Pros and Cons of this approach are the following: Pros: 1) It’s the second fastest of the three options to load Cons: 1) You cannot get rid of the app builder title area without janky css hacks 2) The tab method (outlined below) loads considerably faster. 3) This can load in an iframe depending on your settings. 4) Can’t pass in list view selection ids
LWC List View Button Method 2: The Lightning Component Tab Method
This is my absolute favorite method, it loads ultra fast and is the easiest to setup. If you don’t need list view ids passed into your LWC, this is the way to go in my opinion.
This method works much like the first method, the setup is virtually identical aside from the fact that you setup a Lightning Component Tab to use as opposed to a Lightning App Builder App Page. Even the List View button setup is the same, the only difference is that you use the lightning tabs developer name at the end of the URL. So to save my hands some typing I’m only gonna show you the tab setup, please refer to the rest of the steps in the App Builder setup instruction above.
To setup a tab to use instead of an app builder page is simple. In setup go to Tabs. Then on the Tabs screen, scroll down to the “Lightning Component Tabs” and select the “New” button.
On the new lightning component tab screen select your lightning component (the one shown above or the one you’ve built), enter a tab label, a tab name and select a tab style and you’re done. MAKE SURE YOUR LWC HAS A TARGET IN THE XML FOR TABS (this is shown in the code up above), otherwise it won’t be selectable.
Once you’ve created your tab, just follow the exact steps outlined in the app builder app page scenario to for the lightning button setup and you’re done!
Pros and Cons of this approach: Pros: 1) Fastest load time 2) Easiest setup 3) Never loads in an iFrame Cons: 1) Cannot load in list view ids from selected list view values
LWC List View Button Method 3: The Flow Screen Method
I will urge you to please not use this method unless you absolutely need the selected list view ids passed into your LWC. I say this because the load times are significantly slower and now you have to involve two technologies (flow and lwc) instead of one, making it more complex to deal with, albeit not by a ton.
The steps for setting up the actual list view button for this method are virtually identical to others as well, aside from the URL structure for the list view button, which we will cover, but refer to the first method for setting up the majority of the actual button.
Alrightyyyyy then, here we go. This final method utilizes a flow to allow us to capture the incoming selected list view ids and send them to our LWC to manipulate.
The first thing we need to do is update our LWC a bit to allow it to receive these incoming list view ids, so let’s do thattttttt. I’ll post the code below and then discuss it.
HTML:
<template>
<p>These are the list view ids passed: {listViewIds}</p>
<lightning-button label="Return to List View" onclick={close}></lightning-button>
</template>
First let’s talk about the JavaScript file. We added two things to that file, the first is that we are now importing api at the top so that we can use the api decorator. The second is that we have created the listViewIds variable with the @api decorator on it. This allows the variable to be written to by the flow.
Next let’s talk about the metadata file, in the metadata file we have added a targetConfig. This target config allows us in the flow builder to declaratively assign the incoming list view ids to the LWC’s listViewIds variable.
Last, in the HTML file we have just created a paragraph tag to view the list view ids when they are brought over.
Now that we’ve updated the component, we need to create the flow. In setup, go to Flows and then select “New Flow” at the top to create a new flow.
You will immediately be presented with options for the type of flow you’d like to create, select “Screen Flow” and press the “Next” button., then select the “Freeform” option. You will then land on the flow builder canvas.
The first thing we need to do is create a variable. In the “Toolbox” area on the left side of the screen click the “Manager” tab and then click the “New Resource” button.
After clicking the “New Resource” button a modal will pop-up. Do the following: 1) For Resource Type select “Variable” 2) Fill out the API Name field with the value “ids” (do not include the surrounding quotes). IT MUST BE THIS VALUE TO WORK! 3) For “Data Type” select “Text” 4) Check the, “Allow Multiple Values (Collection)” checkbox 5) Check the, “Available for input” checkbox 6) Click the “Done” button
After setting up this variable you’ll need to grab a screen flow from the “Elements” tab in the toolbox and drag it onto the flow canvas.
On the Screen element modal that pops up you’ll want to do the following: 1) Enter a label and API Name 2) Uncheck the “Show Header” checkbox 3) Uncheck the “Show Footer” checkbox 4) On the left side of the modal in the “Components” area select your LWC and drop it on the page 5) Fill out the API Name for your component 6) place the “ids” variable we created above in the “listViewIds” box for the LWC 7) Click the “Done” button
Then connect your start node to your new screen node, grab the API name of your flow from the settings area of the flow canvas, activate your flow and you’re done!
The one and ONLY step that changes for the list view button setup for the flow variety is the url structure. The URL structure should be changed to the following: /flow/Flow_Developer_Name
Aside from the above, all of the other steps are the same, so please reference the first LWC Button setup method above for more info.
Pros and Cons of this method: Pros: 1) This is the only method that can receive the ids of values selected in a list view
Cons: 1) This is by far the slowest loading method 2) It forces you to use a flow to embed your LWC 3) It’s the most complex setup 4) It hosts itself in an iFrame
Alright, that’s all folks, this blog post was long and my hands are tired. Hasta Luego!!!!!
Get Coding With The Force Merch!!
We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.
What are Data Attributes and Why Should I Use Them?
Data attributes are a truly magical thing that will, at some point, get you out of some prickly situations as a front end developer. They are essentially a way of storing data on an html element so that when a JS event gets fired your JS controller can easily get access to the data on the HTML element that fired the event.
This is especially useful in scenarios where you want to use for:each templates to generate tables, tabs or whatever else on the screen.
How to use Data Attributes (Example Code and Explanation)
Using data attributes is easy peasy lemonnnnnnnnnnn squezzyyyyyyyy… I’m sorry, it just felt right. Seriously though, it’s easy. Let’s first take a look at how we setup our HTML Template to use data attributes on a button element.
<template>
<lightning-button onclick={getDataAttributes} label="Click Me Bruh" data-donkey="donkeysAreKewl" data-turtle="NinjaTurtles"></lightning-button>
</template>
You see those weird attributes on my element? The “data-donkey” and “data-turtle” attributes? Those are data attributes and as you can see they can be named anything! However they must be in this format: data-randomnameyouchoose. Anything can come after the “data-” when setting up the data attributes on your element (do make sure each attribute has a unique name though!). Pretty cool right? The best part comes next though! Let’s check out the JavaScript Controller’s getDataAttributes method.
import {LightningElement} from 'lwc';
export default class LwcDataAttributes extends LightningElement {
getDataAttributes(event){
console.log('This is the data set ::: ' +
JSON.stringify(event.target.dataset));
console.log('This is the data set turtle ::: ' +
JSON.stringify(event.target.dataset.turtle));
}
}
You see that console log that has the “event.target.dataset” value in it? That event.target.dataset produces a Map that houses all of your data attributes in it. The output looks like this:
As you can see it’s a key value pair, the key is whatever you named your data attribute on the HTML Element (notice the data- is excluded however) and the value is whatever value you assigned to that element on your HTML Element.
Now, you may also noticed in the console log below that one that we have the “event.target.dataset.turtle”, this line directly accesses the “data-turtle” value so it will just output “NinjaTurtles”. Pretty niftyyyyy! If you used event.target.dataset.donkey you would get the “donkeysAreKewl” value.
And to be honest that’s really all there is to it, there is one other quick thing we should review though.
What is an Event and the Difference between event.target and event.currentTarget
Boy oh boy does this really confuse people, so let me break it down right quick. Whatever element has the JS event attached to it is the one sending the event parameter to your JS method when you trigger the event (for instance when you use an onclick JS event on an HTML element and then click it to invoke the JS method). THIS IS IMPORTANT! I say this because, it is… trust me, but more importantly you can get into some tricky situations with event.target and event.currentTarget.
The key difference here is that event.target is the TRUE TARGET OF YOUR CLICK! and event.currentTarget is the ELEMENT THAT FIRED THE JS EVENT!
Even if you’ve never done it, you can sometimes wrap multiple elements within a div, and that div is actually the one that houses the onclick event. If you click a button within that div, the button is the “event.target” and the div is the “event.currentTarget”. Be wary of this! If you start to see null values or values you don’t expect in your dataset in your JS controller, this is more than likely why! It has confused many a person (including myself), so just make sure you are paying close attention to which target you are using in your controller.
Get Coding With The Force Merch!!
We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.
Apex Mocks is unit test mocking framework for Apex that was inspired by the very popular Java Mockito framework. The Apex Mocks framework is also built using Salesforce’s Stub API which is a good thing, there are mocking frameworks in existence that do not leverage the Stub API… you shouldn’t use them as they are considerably less performant. In fact, as a remnant of the past, Apex Mocks still has an option to not use the Stub API, don’t use it.
To make this super simple, Apex Mocks is an excellent framework that allows you to not only create mock (fake) versions of your classes for tests, but also allows you to do a number of other things like create mock (fake) records, verify the amount of times a method was called, verify method call order and lots more. There is not another mocking framework for the Apex language that is anywhere near as robust.
Do you have to use the Apex Common Library to use Apex Mocks?
While you don’t have to use the Apex Common Library to use Apex Mocks, they are built to work extremely well together. Without the use of the Apex Common Library you will need to ensure that all of your classes utilize dependency injection. While this isn’t a massive feat, it’s something you have to be cognizant of. If you decide to use the Apex Common library and leverage the the fflib_Application class factory you can leverage the setMock methods to avoid needing to setup all your classes using dependency injection. It’s pretty convenient and makes the whole situation a bit easier. We’ll take a look at both methods below:
Using Apex Mocks without Apex Common Example
//Class we're testing
public with sharing class SoC_Class
{
private DomainClass domainLayerClass;
private SelectorClass selectorLayerClass;
public SoC_Class(){
//This is calling our private constructor below
this(new domainLayerClass(), new selectorLayerClass());
}
//THE MAJOR DIFFERENCE IS HERE! We're using a private constructor here so our test class can pass in dependencies we would
//like to mock in our unit tests
@TestVisible
private SoC_Class(DomainClass domainLayerClass, SelectorClass selectorLayerClass){
this.domainLayerClass = domainLayerClass;
this.selectorLayerClass = selectorLayerClass;
}
public List<Case> updateCases(Set<Id> objectIds){
//Because of our dependency injection in the private constructor above we can mock the results of these class calls.
List<Case> objList = selectorLayerClass.selectByIds(objectIds);
if(!objList.isEmpty()){
List<Case> objList = domainLayerClass.updateCases(objList);
return objList;
}
else{
throw new Custom_Exception();
}
}
}
//Test class
@IsTest
public with sharing class SoC_Class_Test
{
@IsTest
private static void updateCases_OppListResults_UnitTest(){
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
Set<Id> caseIds = new Set<Id>{mockCaseId};
List<Case> caseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Hi', Status = 'New', Origin = 'Email')};
List<Case> updatedCaseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Panther', Status = 'Chocolate', Origin = 'Email')};
fflib_ApexMocks mocks = new fflib_ApexMocks();
DomainClass mockDomain = (DomainClass) mocks.mock(DomainClass.class);
SelectorClass mockSelector = (SelectorClass) mocks.mock(SelectorClass.class);
mocks.startStubbing();
mocks.when(mockSelector.selectByIds(caseIds)).thenReturn(caseList);
mocks.when(mockDomain.updateCases(caseList)).thenReturn(updatedCaseList);
mocks.stopStubbing();
Test.startTest();
//THE MAJOR DIFFERENCE IS HERE! We are passing in our mock classes we created above to the private constructor that is only
//visible to tests to leverage dependency injection for mocking.
List<Case> updatedCases = new SoC_Class(mockDomain, mockSelector).updateCases(caseIds);
Test.stopTest();
System.assertEquals('Panther', updatedCases[0].Subject, 'Case subject not updated');
((Cases)mocks.verify(mockDomain, mocks.never().description('This method was called but it shouldn\'t have been'))).createOpportunities();
((Cases)mocks.verify(mockDomain)).updateCases(caseList);
}
}
If you take a look at the above SoC_Class class you can see that we are leveraging the concept of dependency injection to allow our SoC_Class_Test test class the ability to inject the mock classes during our unit test. This is the key difference. All of your classes MUST LEVERAGE DEPENDENCY INJECTION to be able to incorporate unit test mocking into your codebase. Now let’s take a look at how to do the same thing using the Apex Common Library and the fflib_Application class.
Using Apex Mocks with Apex Common Example
//Example Application factory class. This is only here because it is referenced in the classes below.
public with sharing class Application
{
public static final fflib_Application.UnitOfWorkFactory UOW =
new fflib_Application.UnitOfWorkFactory(
new List<SObjectType>{
Case.SObjectType,
Contact.SObjectType,
Account.SObjectType,
Task.SObjectType}
);
public static final fflib_Application.ServiceFactory service =
new fflib_Application.ServiceFactory(
new Map<Type, Type>{
Task_Service_Interface.class => Task_Service_Impl.class}
);
public static final fflib_Application.SelectorFactory selector =
new fflib_Application.SelectorFactory(
new Map<SObjectType, Type>{
Case.SObjectType => Case_Selector.class,
Contact.SObjectType => Contact_Selector.class,
Task.SObjectType => Task_Selector.class}
);
public static final fflib_Application.DomainFactory domain =
new fflib_Application.DomainFactory(
Application.selector,
new Map<SObjectType, Type>{Case.SObjectType => Cases.Constructor.class,
Contact.SObjectType => Contacts.Constructor.class}
);
}
//Class we're testing
public with sharing class Task_Service_Impl implements Task_Service_Interface
{
public void createTasks(Set<Id> recordIds, Schema.SObjectType objectType)
{
//THE MAJOR DIFFERENCE IS HERE! Instead of using constructors to do dependency injection
//we are initializing our classes using the fflib_Application factory class.
fflib_ISObjectDomain objectDomain = Application.domain.newInstance(recordIds);
fflib_ISObjectSelector objectSelector = Application.selector.newInstance(objectType);
fflib_ISObjectUnitOfWork unitOfWork = Application.UOW.newInstance();
List<SObject> objectsThatNeedTasks = new List<SObject>();
if(objectSelector instanceof Task_Selector_Interface){
System.debug('Selector an instance of tsi');
Task_Selector_Interface taskFieldSelector = (Task_Selector_Interface)objectSelector;
objectsThatNeedTasks = taskFieldSelector.selectRecordsForTasks();
}
else{
System.debug('Selector not an instance of tsi');
objectsThatNeedTasks = objectSelector.selectSObjectsById(recordIds);
}
if(objectDomain instanceof Task_Creator_Interface){
System.debug('Domain an instance of tci');
Task_Creator_Interface taskCreator = (Task_Creator_Interface)objectDomain;
taskCreator.createTasks(objectsThatNeedTasks, unitOfWork);
}
try{
unitOfWork.commitWork();
}
catch(Exception e){
throw e;
}
}
}
public with sharing class Task_Service_Impl_Test
{
@IsTest
private static void createTasks_CasesSuccess_UnitTest(){
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
Set<Id> caseIds = new Set<Id>{mockCaseId};
List<Case> caseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Hi', Status = 'New', Origin = 'Email')};
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_SObjectUnitOfWork mockUOW = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
Cases mockCaseDomain = (Cases) mocks.mock(Cases.class);
Case_Selector mockCaseSelector = (Case_Selector) mocks.mock(Case_Selector.class);
mocks.startStubbing();
mocks.when(mockCaseSelector.sObjectType()).thenReturn(Case.SObjectType);
mocks.when(mockCaseSelector.selectSObjectsById(caseIds)).thenReturn(caseList);
mocks.when(mockCaseSelector.selectRecordsForTasks()).thenReturn(caseList);
mocks.when(mockCaseDomain.sObjectType()).thenReturn(Case.SObjectType);
((fflib_SObjectUnitOfWork)mocks.doThrowWhen(new DmlException(), mockUOW)).commitWork();
mocks.stopStubbing();
//THE MAJOR DIFFERENCE IS HERE! Instead of dependency injection we are using our
//the setMock method in the fflib_Application class to set our mock class for unit tests.
Application.UOW.setMock(mockUOW);
Application.domain.setMock(mockCaseDomain);
Application.selector.setMock(mockCaseSelector);
try{
Test.startTest();
Task_Service.createTasks(caseIds, Case.SObjectType);
Test.stopTest();
}
catch(Exception e){
System.assert(e instanceof DmlException);
}
((Cases)mocks.verify(mockCaseDomain, mocks.never().description('This method was called but it shouldn\'t have
been'))).handleAfterInsert();
((Cases)mocks.verify(mockCaseDomain)).createTasks(caseList, mockUOW);
}
}
As you can see, in this example we no longer leverage dependency injection to get our mock unit tests up and running. Instead we use the setMock method available on all of the inner factory classes in the fflib_Application class to setup our mock class for our unit test. It makes things a bit easier in the long run.
Now that we’ve seen how to setup a mock class with or without Apex Common, let’s figure out all the cool things Apex Mocks has to allow you to assert your logic operated in the way you anticipated it would! We’ll start with a section on what stubbing is below!
What is Stubbing and how to Stub
Soooooo, what exactly is stubbing? Basically it’s the act of providing fake return responses for a mocked class’s methods and it’s super extra important when setting up your unit tests because without them, well frankly nothing is gonna work. So let’s check out how to do stubbing below:
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
List<Case> caseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Hi', Status = 'New', Origin = 'Email')};
//Creating the mock/fake version of our case selector class
fflib_ApexMocks mocks = new fflib_ApexMocks();
Case_Selector mockCaseSelector = (Case_Selector) mocks.mock(Case_Selector.class);
//Here is where we start stubbing our fake return values from our methods that will be called by the actual class we are testing.
//You need to initialize our mock stubbing by using the mocks.startStubbing method.
mocks.startStubbing();
mocks.when(mockCaseSelector.sObjectType()).thenReturn(Case.SObjectType);
mocks.when(mockCaseSelector.selectSObjectsById(caseIds)).thenReturn(caseList);
//This is basically saying, "hey, when the method we're testing calls the selectRecordsForTasks method on our mocked case selector
//please always return this caseList."
mocks.when(mockCaseSelector.selectRecordsForTasks()).thenReturn(caseList);
mocks.stopStubbing();
//Don't forget to stop stubbing!! Very importante!
//Make sure to do your stubbing before sending your mock class to your application factory class!!!
Application.selector.setMock(mockCaseSelector);
//Doing the test of the actual method that will call those mocked class's stubbed methods above. Make sure to create the mock classes and the stubbed
//method responses for your classes before doing this test!
Test.startTest();
Task_Service.createTasks(caseIds, Case.SObjectType);
Test.stopTest();
Alright, let’s go over this a bit (and if you didn’t read the comments in the code outlined above please do!). The first thing you should know is that you should only be creating stubbed methods for methods that are actually called by the real method you are actually testing (in our case, the Task_Service.createTasks method). Now that we’ve clarified that, let’s break down one of our stubs that we built out for the methods in our mocked class. Take this stub ‘mocks.when(mockCaseSelector.selectRecordsForTasks()).thenReturn(caseList);’, what we are efficitively saying here is, when our mockCaseSelector’s selectRecordsForTasks method is called, let’s always return the caseList value. Not too complicated but maybe confusing if you’ve never seen the syntax before.
Hopefully this has helped explain the basic concept of stubbing alright, there’s more we can do with stubbing in regards to throwing fake errors, so be sure to check out the, “How to mock exceptions being thrown” section below for more information on that subject.
How to verify the class you’re actually testing appropriately called your mock class methods
If you’ve never done mocking before this might seem super weird, instead of using asserts, we need another way to verify that our code functioned as anticipated with our fake classes and return values. So what exactly do you verify/assert in your test? What we’re gonna end up doing is verify that we did indeed call the fake methods with the parameters we anticipated (or that we didn’t call them at all). So let’s take a look at how to use the verify method in the fflib_ApexMocks class to verify a method we intended to call, did indeed get called:
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
//Creating a set of ids that we pass to our methods.
Set<Id> caseIds = new Set<Id>{mockCaseId};
//Creating the list of cases we'll return from our selector method
List<Case> caseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Hi', Status = 'New', Origin = 'Email')};
//Creating our mock class representations by using the ApexMocks class's mock method
//and passing it the appropriate class type.
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_SObjectUnitOfWork mockUOW = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
Cases mockCaseDomain = (Cases) mocks.mock(Cases.class);
mocks.startStubbing();
mocks.when(mockCaseDomain.sObjectType()).thenReturn(Case.SObjectType);
mocks.stopStubbing();
Application.UOW.setMock(mockUOW);
Application.domain.setMock(mockCaseDomain);
Test.startTest();
//Calling the method we're actually testing (this is a real method call)
Task_Service.createTasks(caseIds, Case.SObjectType);
Test.stopTest();
//THIS IS IT!!! HERE IS WHERE WE ARE VERIFYING THAT WE ARE CALLING THE CASE DOMAIN CREATE TASKS CLASS ONCE!!!
((Cases)mocks.verify(mockCaseDomain, 1)).createTasks(caseList, mockUOW);
On the very last line of the above code we are using the verify method to ensure that the createTasks method in the Cases class was called exactly one time with the caseList and mockUOW values passed to it. You might be looking at this and thinking, “But why Matt? Why would I actually care to this?”. The answer is pretty simple. Even though your code in those classes isn’t running, you still want to (really need to) verify that your code still decided to call it (or not call it), that you code didn’t call the method more times than you were anticipating, etc. Remember, the major purpose of a unit test is to test that the logic in your class is working how you anticipate it to. Verifying that methods were called correctly is extremely important. If your class calls the wrong methods at the wrong time, super ultra terrible consequences could end up taking place.
How to verify your data was altered by your class when it was sent to a mocked class (Matchers)
Whoooo that’s a long title, lol. Here’s the dealio, at least half the time the data you pass into the class you are actually testing (the one class we aren’t mocking) it will altered and then sent in to another mocked class’s method to be further processed. Chances are you’d like to verify that data was altered prior to sending it to your mocked classes method. Never fear my good ole pals, the fflib_Match class is here to help you do just that! Let’s take a look at how we can use matchers to identify what exactly was passed into our mocked class’s methods!
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
List<Case> caseList = new List<Case>{new Case(
Id = mockCaseId,
Subject = 'Hi',
Status = 'New',
Origin = 'Email')};
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_SObjectUnitOfWork mockUOW = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
Application.UOW.setMock(mockUOW);
Test.startTest();
//Calling the method we're actually testing (this is a real method call). IN THIS METHOD WE ARE CHANGING THE CASE SUBJECTS FROM 'Hi' to 'Bye'
new Cases().changeSubject(caseList);
Test.stopTest();
//Here we are using the fflib_Match class to create a new sobject to match against to verify the subject field was actually changed in the above method //call.
List<Case> caseMatchingList = (List<Case>)fflib_Match.sObjectsWith(new List<Map<Schema.SObjectField, Object>>{new Map<SObjectField, Object>{
Case.Id => mockCaseId,
Case.Subject => 'Bye',
Case.Status => 'New',
Case.Origin => 'Email'}
});
//Here we are verifying that our unit of works registerDirty method was indeed called with the updated data we expected by using a matcher. This should //return true pending your code actually did update the cases prior to calling this method as you intended.
((fflib_ISObjectUnitOfWork)mocks.verify(mockUOW)).registerDirty(caseMatchingList);
//This also works (confusing right)... but it proves a lot less, it simply proves your method was called with that list of cases, but it doesn't prove it //updated those cases prior to calling it.
((fflib_ISObjectUnitOfWork)mocks.verify(mockUOW,1)).registerDirty(caseList);
Alright alright alright, you might be looking at this and being like, “But bruh why make a matcher at all to get this done? Why not just keep it simple and make another list of cases to represent the changes”. Fair enough question hombre, here’s the simple answer… it’s not gonna work, trust me, lol. Basically ApexMocks needs a way to internally check these events actually occurred and that matcher we setup is the key to doing that. If you wanna dive deep into how it works feel free, but that is a whole different subject we’re not gonna get into the weeds of.
So we can see, I think, that this is extremely useful in terms of verifying our logic occurred in the right order and updated our data as anticipated before calling the method in one of our mocked classes. Pretty cool, makes our unit testing that much more accurate.
There are a ton more methods in the fflib_Match class that you can leverage to do virtually whatever you can think of, there’s a list of those near the bottom of this page. Definitely check them out.
How to mock exceptions being thrown (this is THA BEST!!)
If you don’t care about time savings for test runs to get your code into production, you should pick up mocking to allow for easier exception testing at the very least. IT IS SO MUCH EASIER TO VERIFY EXCEPTIONS!!!! A magical thing that honestly can’t be beat. Tons of errors that you should rightfully handle in a ton of situations (like record locking errors) are borderline impossible to test with real data… this makes it possible. Some people out there might be like, “Hah, but I don’t even catch my exceptions why should I care?”. To that I say, time to change my guy, lol, you need to catch those bad boiz, trust me. Anywhooo, let’s get right to it! How do we mock our exceptions to make testing exceptions so simple and so glorious? Let’s checkout the code below:
Here’s an example of mocking an error when the method returns nothing:
fflib_ApexMocks mocks = new fflib_ApexMocks();
fflib_SObjectUnitOfWork mockUOW = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
mocks.startStubbing();
//This right here is what's doing the mock error throwing!! Basically we're saying, when the commitWork method in our
//mockUOW class is called by the method we are truly testing, please throw a new DMLException.
((fflib_SObjectUnitOfWork)mocks.doThrowWhen(new DmlException(), mockUOW)).commitWork();
mocks.stopStubbing();
Application.UOW.setMock(mockUOW);
try{
Test.startTest();
//Calling the method we're actually testing (this is a real method call)
Task_Service.createTasks(caseIds, Case.SObjectType);
Test.stopTest();
}
catch(Exception e){
//Because we are throwing an exception in our stubs we need to wrap our real
//method call in a try catch and figure out whether or not it actually threw the
//exception we anticipated it throwing.
System.assert(e instanceof DmlException);
}
Here’s a similar example but with a method that returns data:
fflib_ApexMocks mocks = new fflib_ApexMocks();
Case_Selector mockCaseSelector = (Case_Selector) mocks.mock(Case_Selector.class);
mocks.startStubbing();
//These two methods must be stubbed for selectors if using apex common library
mocks.when(mockCaseSelector.sObjectType()).thenReturn(Case.SObjectType);
mocks.when(mockCaseSelector.selectSObjectsById(caseIds)).thenReturn(caseList);
//This right here is what's doing the mock error throwing!! Basically we're saying, when the selectRecordsForTasks method in our
//mockCaseSelector class is called by the method we are truly testing, please throw a new DMLException.
mocks.when(mockCaseSelector.selectRecordsForTasks()).thenThrow(new DmlException());
mocks.stopStubbing();
Application.selector.setMock(mockCaseSelector);
try{
Test.startTest();
//Calling the method we're actually testing (this is a real method call)
Task_Service.createTasks(caseIds, Case.SObjectType);
Test.stopTest();
}
catch(Exception e){
//Because we are throwing an exception in our stubs we need to wrap our real
//method call in a try catch and figure out whether or not it actually threw the
//exception we anticipated it throwing.
System.assert(e instanceof DmlException);
}
You can see that there’s a small difference between the two. For the method that actually returns data the chain of events is a bit easier to follow mocks.when(mockCaseSelector.selectRecordsForTasks()).thenThrow(new DmlException());. This basically is just saying, when we call the mock selector class’s selectRecordsForTasks class then we need to throw a new DMLException.
For the method that doesn’t return any data (a void method) the syntax is a little different ((fflib_SObjectUnitOfWork)mocks.doThrowWhen(new DmlException(), mockUOW)).commitWork(). You can see that instead of using the mocks.when(method).thenThrow(exception) setup we are now using the doThrowWhen method, which kinda combines the when(method).thenThrow(exception) into a single statement. You can also see it takes two parameters, the first is the exception you want to throw and the second is the mocked class you are attaching this exception throwing event to. Then you have to cast this mocks.doThrowWhen(exception, mockedClass) to the class type it will end up representing, wrap it in parenthesis and call the method you intend it to throw an error on. Weird setup, awesomely simple exception testing though.
Also worth noting, don’t forget to wrap methods you are truly testing (that will now throw an error) in a try catch in your test and in the catch block, assert that you are getting your exceptions throw.
How to test important method side-effects truly occured (Answering)
It is not at all uncommon to have parameters passed into a method by reference and for that method to update those parameters but not pass them back. Some people refer to this as method side effects. To truly do good unit tests however we should mock those side effects as well. That is where the fflib_Answer interface comes into play! Now, unlike most things I’ve written about in this series, there is a blog post by Eric Kintzer that already exists for this apex mocks topic that is excellent and well… frankly I don’t need to re-invent the wheel here. Please go check out how to do Answering with Apex Mocks here. .
How to check method call order
Sometimes when you are doing unit tests it’s extremely important to verify the order in which your methods were called. Maybe, depending on what data your method receives your mock class’s methods could be called in varying orders. It’s best to check that the code is actually doing that! If you do need to check for that in your code, no worries, that is why the fflib_InOrder class exists! Thankfully this is yet another area I really don’t need to cover again in this guide as a good blog post exists covering this topic as well! If you need to check method ordering in your unit testing, please check out the blog post here!
Apex Mocks Counters (Counting method calls and adding descriptions)
Counters counters counters… they’re one of the simplest things to learn thankfully and also very useful! Basically you might want to know how many times a method in a mock class was called by the class you are currently unit testing. It helps to verify the logic in your code is only calling classes in the way you intended for it to call it. The syntax is pretty simple so lets get to learninnnnnnnn.
When we are verifying method calls (as discussed in one of the above sections) we do the following:
What if we wanna check whether the code operated only once though? We can check pretty easy by passing a second parameter to the verify method like so:
The above code will verify that our method was not just called, but only called once. Pretty kewl right? But wait! There’s more! Let’s check out a bunch of different counter scenarios below.
How to verify a method was called at least x times:
Wow wow wow, so many options for so many scenarios, however I would be remiss if I didn’t also should you the description method call you can chain to basically give you useful verification failure statements. Let’s check that out right quick:
((fflib_ISObjectUnitOfWork)mocks.verify(mockUOW, mocks.times(2).description('Whoa champ, you didnt call this dirty method twice, try again broseph'))).registerDirty(caseMatchingList);
If your verification fails (and in turn your test class fails) you will now get that wonderful description to help you pinpoint the cause of your test failure a bit easier. That’s pretty awesome opposum.
If we couldn’t produce fake records to pass to our mocked class methods to handle, honestly this whole setup would only be kinda good because we’d always be forced to do real DML transactions in our tests to create data, and if you didn’t know the reason tests can take sooooooooooooo long is because of DML transactions. They take a crazy amount of time in comparison to everything else in your code (unless you’ve got quadruple inner for loops in your codebase somewhere… which you should address like right now, please stop reading this and fix it). So let’s figure out how to make fake data using the Apex Mocks Library.
The first class on our list in the Apex Mocks library to check out is the fflib_IDGenerator class. Inside this class there is a single method, generate(Schema.SObjectType sobjectType) and its purpose is to take an SObjectType and return an appropriate Id for it. This is super useful because if you need to fake that a record already exists in the system, or that a record is parented to another record, you need an Id. You’ll likely use this method a ton.
Example method call to get a fake (but legal) Id:
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
That will return to you a legal caseId! Pretty useful.
The next class on our list to cover here is the fflib_ApexMocksUtils class. This class has three accessible methods and they allow you to make in memory relationships between fake records and to setup formula field values for a fake record. Let’s take a look at some examples below.
//Example from: https://salesforce.stackexchange.com/questions/315832/mocking-related-objects-using-fflib
Opportunity opportunityMock1 = new Opportunity(Id = fflib_IDGenerator.generate(Opportunity.SObjectType));
//This basically creates a list of opportunities with child opportunity line items in the list in the OpportunityLineItems field
List<Opportunity> opportunitiesWithProductsMock = (List<Opportunity>) fflib_ApexMocksUtils.makeRelationship(
List<Opportunity>.class,
new List<Opportunity>{
opportunityMock1
},
OpportunityLineItem.OpportunityId,
new List<List<OpportunityLineItem>>{
new List<OpportunityLineItem>{
new OpportunityLineItem(Id = fflib_IDGenerator.generate(OpportunityLineItem.SObjectType)),
new OpportunityLineItem(Id = fflib_IDGenerator.generate(OpportunityLineItem.SObjectType))
}
}
);
//This is basically the same method as above, but you can pass in String to determine the object and relationship instead of types. I would
//use the method above unless this one was 110% necessary.
List<Opportunity> opportunitiesWithProductsMock = (List<Opportunity>) fflib_ApexMocksUtils.makeRelationship(
'Opportunity',
'OpportunityLineItem',
new List<Opportunity>{
opportunityMock1
},
'OpportunityId',
new List<List<OpportunityLineItem>>{
new List<OpportunityLineItem>{
new OpportunityLineItem(Id = fflib_IDGenerator.generate(OpportunityLineItem.SObjectType)),
new OpportunityLineItem(Id = fflib_IDGenerator.generate(OpportunityLineItem.SObjectType))
}
}
);
//Code from: https://github.com/apex-enterprise-patterns/fflib-apex-mocks
Account acc = new Account();
Integer mockFormulaResult = 10;
//This will allow you to set read only fields on your objects, such as the formula field below. Pretty useful!
acc = (Account)fflib_ApexMocksUtils.setReadOnlyFields(
acc,
Account.class,
new Map<SObjectField, Object> {Account.Your_Formula_Field__c => mockFormulaResult}
);
Example Apex Mocks Classes
Task_Service_Impl_Test – Apex Mocks test class example that uses the Apex Common library.
Next Section
That’s it! You’re done! Thank god… I’m tired of writing this thing. You can go back to the home page here.
How does Unit Testing fit into Separation of Concerns?
The answer to this is simple, without Separation of Concerns, there is no unit testing, it just simply isn’t possible. To unit test you need to be able to create stub (mock) classes to send into your class you are testing via dependency injection (or through the use of a factory, more on this in the next section). If all of your concerns are in one class (DML transactions, SOQL queries, service method, domain methods, etc) you cannot fake anything. Let’s take a look at a couple examples to illustrate this problem:
Unit Testing a class with SoC Implemented
//This is the class we would be testing
public with sharing class SoC_Class
{
private DomainClass domainLayerClass;
private SelectorClass selectorLayerClass;
public SoC_Class(){
//This is calling our private constructor below
this(new domainLayerClass(), new selectorLayerClass());
}
//Using a private constructor here so our test class can pass in dependencies we would
//like to mock in our unit tests
@TestVisible
private SoC_Class(DomainClass domainLayerClass, SelectorClass selectorLayerClass){
this.domainLayerClass = domainLayerClass;
this.selectorLayerClass = selectorLayerClass;
}
public List<Case> updateCases(Set<Id> objectIds){
//Because of our dependency injection in the private constructor above we can
//mock the results of these class calls.
List<Case> objList = selectorLayerClass.selectByIds(objectIds);
if(!objList.isEmpty()){
List<Case> objList = domainLayerClass.updateCases(objList);
return objList;
}
else{
throw new Custom_Exception();
}
}
}
//This is the class that we build to unit test the class above
@IsTest
public with sharing class SoC_Class_Test
{
@IsTest
private static void updateCases_OppListResults_UnitTest(){
//Creating a new fake case id using the IdGenerator class. We do this
//to avoid unnecessary dml insert statements. Note how the same id is used
//everywhere.
Id mockCaseId = fflib_IDGenerator.generate(Case.SObjectType);
//Creating a set of ids that we pass to our methods.
Set<Id> caseIds = new Set<Id>{mockCaseId};
//Creating the list of cases we'll return from our selector method
List<Case> caseList = new List<Case>{new Case(Id = mockCaseId, Subject = 'Hi',
Status = 'New', Origin = 'Email')};
List<Case> updatedCaseList = new List<Case>{new Case(Id = mockCaseId, Subject
= 'Panther', Status = 'Chocolate', Origin = 'Email')};
//Creating our mock class representations by using the ApexMocks class's mock
//method and passing it the appropriate class type.
fflib_ApexMocks mocks = new fflib_ApexMocks();
DomainClass mockDomain = (DomainClass) mocks.mock(DomainClass.class);
SelectorClass mockSelector = (SelectorClass) mocks.mock(SelectorClass.class);
//After you've setup your mocks above, we need to stub (or setup the expected
//method calls and what they would return.
mocks.startStubbing();
//This is the actual selectByIds method that we call in the
//createNewOpportunities method that we are testing
//Here we are setting up the fake return result it will return.
mocks.when(mockSelector.selectByIds(caseIds)).thenReturn(caseList);
mocks.when(mockDomain.updateCases(caseList)).thenReturn(updatedCaseList);
//When you are done setting these up, DO NOT FORGET to call the stopStubbing
//method or you're gonna waste hours of your life confused
mocks.stopStubbing();
Test.startTest();
//Passing our mock classes into our private constructor
List<Case> updatedCases = new SoC_Class(mockDomain,
mockSelector).updateCases(caseIds);
Test.stopTest();
System.assertEquals('Panther', updatedCases[0].Subject,
'Case subject not updated');
//Verifying this method was never called, we didn't intend to call it, so
//just checking we didn't
((Cases)mocks.verify(mockDomain, mocks.never().description('This method was
called but it shouldn\'t have been'))).createOpportunities();
//Checking that we did indeed call the createTasks method as expected.
((Cases)mocks.verify(mockDomain)).updateCases(caseList);
}
}
Above you can see we are passing in fake/mock classes to the class we are testing and staging fake return results for the class methods we are calling. Thanks to separating out our concerns this is possible. Let’s take a look at how impossible this is without SoC in place.
Unit Testing a class without SoC Implemented
//This is the class we would be testing
public with sharing class No_SoC_Class
{
public List<Case> updateCases(Set<Id> objectIds){
//Because of our dependency injection in the private constructor above we can
//mock the results of these class calls.
List<Case> objList = [SELECT Id, Name FROM Case WHERE Id IN: objectIds]
if(!objList.isEmpty()){
for(Case cs: objList){
cs.Subject = 'Panther';
cs.Status = 'Chocolate';
}
update objList;
return objList;
}
else{
throw new Custom_Exception();
}
}
}
@IsTest
public with sharing class No_SoC_Class_Test
{
@TestSetup
private static void setupData(){
Case newCase = new Case(Subject = 'Hi', Status = 'New', Origin = 'Email');
insert newCase;
}
@IsTest
private static void updateCases_CaseListResults_IntegrationTest(){
Set<Id> caseIds = new Map<Id, SObject>([SELECT Id FROM Case]).keySet();
List<Case> updatedCases = new No_SoC_Class().updateCases(caseIds);
System.assertEquals('Panther', updatedCases[0].Subject, 'Case subject not
updated');
}
}
You can see above we did no mocking… it wasn’t possible, we had no way of passing in fake/mock classes to this class at all so we had to do an integration test where we create real data and update real data. This test will run considerably slower.
How do I transition my Existing Code to start leveraging SoC so I can use Unit Test Mocking?
It’s not a simple path unfortunately, there is a lot of work ahead of you to start this transition, but it is possible. The key is to start small, if you are a tech lead the first thing you need to do is find the time to train your devs on what SoC and mocking is and why you would use it. It’s critical they understand the concepts before trying to roll something like this out. You cannot do everything as a lead even if you’d like to, you need to build your team’s skillset first. If you aren’t a lead, you first need to convince your lead why it’s critical you start taking steps in that direction and work to get them onboard with it. If they ignore you, you need a new lead… After accomplishing either the above you should do the following:
1) Frame your situation in a way that the business arm of your operation understands the importance of altering your code architecture to leverage SoC and unit testing. This is typically pretty easy, just inform them that by spending a few extra points per story to transition the code you are going to gain more robust testing (resulting in less manual tests) and that the code will become more flexible over time, allowing for easier feature additions to your org. Boom, done, product owner buy in has been solidified. Ok, lol, sometimes it’s not quite this easy, but you know your business people, give them something they won’t ignore, just be careful to not make yourself or your team sound incompetent, don’t overcommit and make promises you can’t keep and frame it in words business people understand (mostly dollar signs).
2) Start small, take this on a story by story basis. Say you have a new story to update some UI in your org, with business owner buy-in, tack on a few extra points to the story to switch it to start using SoC and Unit Testing. Then, MAKE SURE YOUR TESTS ARE INCREDIBLE AND YOU TRUST THEM! I say this because, if your tests are incredible you never have to ask for permission again… I mean think about it, wtf does the average business person know about code. As long as I don’t introduce any new bugs I could change half the code base and they’d never even know. If your tests are golden, your ability to change on a whim is as well. Make those test classes more trustworthy than your dog (or cat I guess… but I wouldn’t trust a cat).
3) Over time, your transition will be done… it might take a year, it might take 5 years, depends on how busted your codebase was to begin with. Hopefully, by the time you are done, you have an extremely extensible codebase with tests you can trust and you never have to ask for permission to do a damn thing again.
Unit Testing, is a way to test the logic in one class and ONLY one class at a time. Most classes in your system at some point do one of the following:
1) Call to another class to get some work done. 2) Query the database directly in the class. 3) Do DML transactions directly in the class.
However, in unit testing, we ideally don’t want to do any of those things, we don’t care if the querying or the DML transactions or the calls to those other classes work, because what we want to test is that the logic of our class works in every theoretical permutation we can think of AND THAT’S IT! We just want to know if our logic, that we designed for this one class actually works as we anticipate it to work.
You might be thinking, “How is that possible? We need those queries and those dml statements and those classes to successfully run our code!”. Well that my friends is simply not true. With the implementation of separation of concerns and by leveraging one of the many available mocking frameworks we can fake all of those things to build true unit tests.
When Should I use Unit Testing?
Just because you are leveraging unit testing doesn’t mean there still isn’t a need for integration tests (more on that below), so when should you do Unit Tests in favor of Integration tests? It’s a pretty simple answer. The logic in your class’s code could have 40+ paths it could take… maybe even 100… The code in your class could have logic that runs when your code fails, logic that runs when your codes successful, logic that runs when someone creates a $200,000,000 opportunity as opposed to a $2,000 opportunity. There could be so many paths. We DO NOT need an integration test for every single one of those paths. It could take 20+ minutes to run integration tests for all those paths whereas unit testing them could take just seconds. As your codebase grows, if you didn’t make these unit tests your test class runs to deploy to prod could take hours… maybe days… we don’t want that homie. So when do you unit test? You unit test to test the bajillions of permutations of your class’s logic. When do we choose integration tests?? Keep reading. You’ll find out below.
An additional benefit to unit testing/mocking responses is testing hard to test or impossible to test error catching scenarios. One common scenario that I find myself frequently building error catching around is record locking… but how on earth can you test that with real data at run time? It’s borderline impossible, however unit testing makes testing for errors a breeze. It’s so easy you’ll cry, lol, the dream of 100% code coverage can go from just a dream to a very real and easily obtainable thing with unit tests and mocking.
What is Integration Testing?
Integration testing is when you test your code from point A all the way to point Z. What I mean by that is, you actually have your code in your test classes doing SOQL statements, DML transactions, calling the other classes it depends on, etc. Basically your code is truly calling all the real logic from beginning to end, no mocking or fake return results anywhere. Integration testing is what 90%+ of Salesforce orgs seem to do 100% of the time and that is one dangerous game to play. The majority of your tests should not be integration tests… maybe like 20% of them or so. If 100% of your test classes are integration tests, over time you’ll suffer from long test execution and deployment times (or alternatively poorly tested code to reduce those times). If you’ve ever been stuck in a 6+ hour deployment, I think you know what I mean… and what if one test fails during that deploy?… it really hurts, lol. Please, don’t get me wrong though, you can not replace integration tests with unit tests, they are of equal importance, it’s just important that you only use integration tests when needed.
When Should I use Integration Testing?
All apex classes should have some level of integration testing. I like to write somewhere around 20% of my tests as integration tests and 80% of my tests for a class as unit tests. Typically I will, for each class that has dependencies (classes that the class I’m currently testing calls) write two integration tests for each dependency. One integration test that tests a successful path through my classes code and its dependencies and another that tests failures. This may be, in some peoples opinion, overkill, but I personally feel that it’s not an enormous amount of overhead (typically) and it gives me some piece of mind that all of the classes my class I’m testing depends on still operate well with it. So, when to use integration testing? In my opinion, in every test class, but use them sparingly, use them to check that your transactions between classes still work, don’t use them to test every logical path in your class’s code. That will spiral out of control.
Every Selector layer class you create should at least implement the following methods for it to work as anticipated.
//Selector layer classes should all use the inherited sharing keyword so that the caller //determines to context it operates in.
//It should also always extend the fflib_SObjectSelector so it can inherit that classes //methods and functionality.
public inherited sharing class Contact_Selector extends fflib_SObjectSelector
{
public Contact_Selector(){
/*This is calling the fflib_SObjectSelector classes constructor and setting
the following booleans:
1) If the selector queries should use field sets
2) If you would like to enforce CRUD security
3) If you would like to enforce FLS
4) If you would like to sort selected fields
*/
super(false, true, true, false);
}
//Add the base fields for the object that should just be used in absolutely every
//query done by this class
public List<Schema.SObjectField> getSObjectFieldList(){
return new List<Schema.SObjectField>{
Contact.Id,
Contact.Name,
Contact.FirstName,
Contact.LastName
};
}
//Allows you to easily get the object type for the object being queried by this class
public Schema.SObjectType getSObjectType(){
return Contact.SObjectType;
}
//Allows you to create a query that selects records by a set of ids (basically adds
//the WHERE Id IN :ContactIds to the query)
public List<Contact> selectById(Set<Id> contactIds){
return (List<Contact>) selectSObjectsById(contactIds);
}
}
The fflib_SObjectSelector Constructor Parameters and what each of them mean
When you create a Selector class that extends the fflib_SObjectSelector class you have the option to send some parameters to its constructor that determine how to the selector class functions. They are the following (in order of parameters passed to the constructor):
1) Would you like to allow your class to use field sets when building the initial query fields for your selector layer class? By default this parameter is set to false.
2) Would you like to enforce CRUD (Object level create, read, update, delete) security on your selector layer class? By default this parameter is set to true.
3) Would you like to enforce FLS (Field level security) on your selector layer class? By default this parameter is set to false.
4) Would you like your selected fields in your query to be sorted alphabetically when your query is created (this is literally just a sort call on a list of strings in the code)? By default this parameter is set to true.
If you would like to alter any of these settings for your selector class you can do the following:
public inherited sharing class Contact_Selector extends fflib_SObjectSelector
{
//In our selector classes constructor we are calling the fflib_SObjectSelector using the super() call
//and setting out parameters.
public Contact_Selector(){
//This is completely optional. If you like the defaults listed above you do not have to call the super class constructor at all.
super(false, true, true, false);
}
}
Note that this is completely optional, if you like the default behavior listed above, just don’t bother calling the super method and overriding them!
How to set the default field selection for your Selector Class
One of the biggest benefits of the selector layer is selected field consistency in your queries. If 99% of the time you are querying for the subject field on your Case object, why not just query for it by default without ever thinking about it again right?? Well the getSObjectFieldList method that you implement in your Selector classes does just that. Here’s how to implement that method in your selector:
public List<Schema.SObjectField> getSObjectFieldList(){
//Note that this is using concrete references to the fields, not dynamic soql (here at
//least). This is to ensure the system knows
//your code is dependent on these fields so you don't accidentally delete them some
//day.
return new List<Schema.SObjectField>{
//In this list, place every field you would like to be queried for by
//default when creating a query
//with your selector class.
Contact.Id,
Contact.Name,
Contact.FirstName,
Contact.LastName
};
}
If you choose to enable field sets to allow for default field selection for your selector class, you can implement the following method to select fields from the field set to be included in your queries:
public override List<Schema.FieldSet> getSObjectFieldSetList(){
return new List<Schema.FieldSet>{SObjectType.Case.FieldSets.CaseFieldSetForSelector};
}
The major benefit of using the field sets for query fields is you can add new fields on the fly without adding extra code. This becomes super important if you’re building a managed package.
How to set your default OrderBy clause in your Selector Class
By default all Selector Layer Classes that extend the fflib_SObjectSelctor are ordered by the Name field (if the name field isn’t available on the queried object it defaults to CreatedDate). If that’s kewl with you, no need to override it, but if you’d prefer the default sort order for your select class be different then you just need to override the following method like so:
public override String getOrderBy(){
return 'Subject';
}
If you wanted to order by multiple fields you would just do the following (basically just create a comma separated list in the form of a string):
public override String getOrderBy(){
return 'Subject, Name, CustomField__c, CustomLookup__r.Name';
}
The fflib_QueryFactory class and Custom Queries
Chances are you’re gonna wanna query something that’s not immediately available via the fflib_SObjectSelect class, well lucky for you the fflib_QueryFactory class is here just for that! Here is an example of a custom query via the use of the fflib_QueryFactory class (a list of all available query factory methods are at the bottom of this wiki article).
//This allows us to select all new cases in the system using the QueryFactory in //fflib_SObjectSelector
public List<Case> selectNewCases(){
return (List<Case>) Database.query(newQueryFactory().
selectField(Case.Origin).
setCondition('Status = \'New\'').
setLimit(1000).
toSOQL());
}
There are also tons of other methods that allow you to add offsets, subselects, set ordering and more (outlined in the method cheat sheet section).
How to Do Sub-Select Queries (Inner Queries)
There’s a kinda handy way to do sub-select queries by using the fflib_QueryFactory class. You can use it in your selector class’s methods as outlined below:
//Pretend this method exists in a Case_Selector class
public List<Case> innerQueryExample(){
//Using this Case_Selectors newQueryFactory method that it inherits from the
//fflib_SObjectSelector class it extends
fflib_QueryFactory caseQueryFactory = newQueryFactory();
//Creating a new instance of our Task_Selector class and using the
//addQueryFactorySubselect method to add the task query as a
//inner query for the caseQueryFacorty
fflib_QueryFactory taskSubSelectQuery = new
Task_Selector().addQueryFactorySubselect(caseQueryFactory);
//Querying for our data and returning it.
return (List<Case>) Database.query(caseQueryFactory.toSOQL());
}
How to do Aggregate Queries
There’s not really a built-in method in either the fflib_SObjectSelector or the QueryFactory class to deal with this. So you just deal with aggregate queries by building a method in your Selector Class in the following way:
public List<AggregateResult> selectAverageTacosPerTacoBell(){
List<AggregateResult> tacoData = new List<AggregateResult>();
for(AggregateResult result: [SELECT Id, AVG(Tacos_Sold__c) avgTacos FROM Taco_Bell__c
GROUP BY Taco_Type__c]){
tacoData.add(result);
}
return tacoData;
}
The fflib_SObjectSelector Class methods Cheat Sheet
While there are many other methods within the fflib_SObjectSelector class below are the methods most commonly utilized in implementations.
1) getSObjectName()_ – Returns the name of the object that your select was built for.
2) selectSObjectsById(Set<Id> idSet) – Returns a query that selects all the records in your id set. It constructs a query based on the fields you declare in your selector class’s getSObjectFieldsList method and orders them by whatever is represented in the m_orderBy variable.
3) queryLocatorById(Set<Id> idSet) – This method is basically the same as the selectSObjectsById method except that it returns a query locator instead. This should be leveraged for batch classes that need query locators.
4) newQueryFactory – This method returns a new instance of the fflib_QueryFactory class and uses your selector classes fields listed in the getSObjectFieldsList method and orders them by whatever you set the default orderby to in your selector class.
5) addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory) – This method basically just creates a subselect query (an inner query) for the “parentQueryFactory” and then returns the parent query with a subselect query that represents the query for your objects selector class (more info on how this method works in the sub-select query section in this wiki article).
The fflib_QueryFactory Class methods Cheat Sheet
While there are many other methods within the fflib_QueryFactory class below are the methods most commonly utilized in implementations.
1) assertIsAccessible() – This method checks to see if the running user can actually read to object this selector was built for.
2) setEnforceFLS(Boolean enforce) – This method determines whether or not to enforce field level security on the query you are running.
3) setSortSelectFields(Boolean doSort) – If you would like the list of fields selected to be selected in alphabetical order, you can use this method to make your selected fields be selected in alphabetical order.
7) setCondition(String conditionExpression) – Use this method to set the conditions for your query (basically you are setting the WHERE clause in your query. Do NOT add the WHERE to the string you pass into to this method).
10) addOrdering(Ordering o) – Sets up an ORDER BY clause for your query. You need to build an fflib_QueryFactory.Ordering object to pass to this method. There are a ton of ordering method permutations in this class, so definitely check them all out.
11) subselectQuery(String relationshipName) – Sets up a subquery for the query. You need to pass in a string representation of the relationship fields developer name to this method.
12) toSOQL() – Takes the fflib_QueryFactory you’ve just built and turns it into an actual string representation of the query that you can use in a Database.query() call.
13) deepClone() – Creates a clone of your fflib_QueryBuilder query in case you’d like an easy way to generate another permutation of this query without rebuilding the whole thing.
The Selector Layer is Salesforce is based off Martin Fowler’s Data Mapper Layer concept. It’s, “a layer of Mappers that moves data between objects and a database while keeping them independent of each other and the Mapper itself”.
In most tech stacks when you want to represent records in a database table you create classes in your code base to represent them to hold them in memory and prep them for transit to the actual database. These are what Martin references as “objects” in the above quote. Salesforce already creates these classes for you in the background to represent your Custom and Standard Objects. It’s why you can just inherently write Case cs = new Case(); in Apex and it creates a brand new Case record for you.
Since Salesforce already does that work for you, the Data Mapper Layer, just turns into the Selector Layer and within the Selector Layer we are basically just concerned with housing and performing queries for our respective objects. You will ideally call upon the Selector Layer every single time you need to make a query in Apex. The largest goal with this layer is to avoid having repetitive queries everywhere within the system and to have some overall consistency with your object queries (the fields always queried, limits, order by clause, etc).
When to make a new Selector Layer Class
Whenever you need to create queries on an object you’ve never done queries on before, you would create a new Selector Layer Class. So for instance if you needed to create some queries for the case object to use in your service or domain layer classes, you would create a Case Selector class. There should typically be one selector class per object you intend to query on.
Selector Layer Naming Conventions
Class Names – Your classes should ideally follow the naming conventions of the domain layer just with Selector appended to the end of them, unless you have common cross object queries then it’s a bit different.
Selector Class Naming Examples (Note that I prefer underscores in names, this is personal preference):
Method Naming and Signatures – The names of the methods in a selector class should all start with the word “select”. They should also only return a list, map or QuerySelector and should only accept bulkified parameters (Sets, Lists, Maps, etc). A few good examples of method examples are below.
Selector Method Examples
public List<sObject> selectById(Set<Id> sObjectIds)
public List<sObject> selectByAccountId(Set<Id> accountIds)
public Database.QueryLocator selectByLastModifiedDate(Date dateToFilterOn)
Selector Layer Security
The Selector Layer classes should all ideally inherit their sharing from the classes calling them. So they should typically be declared as follows:
public inherited sharing class Account_Selector
If there are queries for your object that you absolutely must have run in system context (without sharing) you would want to elevate those permissions through the use of a private inner class like so:
public inherited sharing class Account_Selector
{
public List<Account> selectAccountsById(Set<Id> acctIds){
return new Account_Selector_WithoutSharing().selectAccountsByIdElevated(acctIds);
}
private without sharing Account_Selector_WithoutSharing{
public List<Account> selectAccountsByIdElevated(Set<Id> acctIds){
return [SELECT Id FROM Account WHERE Id IN : acctIds];
}
}
}
Implementing the Selector Layer
To find out how to implement the Selector Layer using Apex Commons, continue reading here: Implementing the Selector Layer with the Apex Common Library . If you’re not interested in utilizing the Apex Common library for this layer there are pretty sparing options out there that are pre-built, the only other option I’m aware of at this time is Query.apex. Outside of that, you could certainly roll your own selector layer, but it is no small feat if done right.
The Builder Pattern is a Creational Design Pattern that allows you to construct a complex object one step at a time. Think about the construction of a car or your house or maybe something less complicated, like the construction of a desktop computer. When you’re creating a new desktop computer you have to make a ton of selections to build the computer. You have to get the right cpu, motherboard, etc. Instead of passing all that selection information into a class that constructed the computer, it’d be a lot nicer to build it step by step. Let’s check out some examples:
Non-Builder Pattern Computer Building Class Example:
public class ComputerBuilderController(){
public ComputerCreator createMidTierComputer(){
return new ComputerCreator(midTierGraphicsCard, midTierCPU, midTierMotherboard,
midTierFan, null, null).getComputer();
}
public ComputerCreator createTopTierComputer(){
return new ComputerCreator(topTierGraphicsCard, topTierCPU, topTierMotherboard,
topTierFan, topTierNetworkCard, null).getComputer();
}
}
public class ComputerCreator{
private CPU compCPU;
private GPU compGPU;
private MotherBoard compMotherBoard;
private Fan compFan;
private NetworkCard compNetworkCard;
private Speakers compSpeakers;
//This could go on for forever and you might have 300 different constructors with
//different variations of computer parts... This could become
//an absolutely enormous class.
public ComputerCreator(GraphicsCard selectedGraphicsCard, CPU selectedCPU, MotherBoard
selectedMotherboard, Fan selectedFan, NetworkCard selectedNetworkCard, Speakers
selectedSpeakers){
setComputer(selectedGraphicsCard, selectedCPU, selectedMotherboard, selectedFan,
selectedNetworkCard, selectedSpeakers);
}
//Because of how this is setup, we're setting everything for the computer, even if we're
//just setting the computer parts to null
private void setComputer(GraphicsCard selectedGraphicsCard, CPU selectedCPU, MotherBoard
selectedMotherboard, Fan selectedFan, NetworkCard selectedNetworkCard, Speakers
selectedSpeakers){
this.compGPU= selectedGraphicsCard;
this.compCPU = selectedCPU;
this.compMotherBoard = selectedMotherboard;
this.compFan= selectedFan;
this.compNetworkCard = selectedNetworkCard;
this.compSpeakers = selectedSpeakers;
}
public ComputerCreator getComputer(){
return this;
}
}
You can see in the above example, this setup is not exactly ideal, nor does it lend itself to easy code changes. If you go with the single constructor approach and just allow developers to pass in nulls to the constructor, every time you need to add another option for the computers your code might build, you’ll have to update every piece of code that calls the ComputerCreator class because the constructor will change. Alternatively if you go with new constructor variations for each new option you could end up with hundreds of constructors over time… also not great at all. That can be extremely confusing and difficult to upkeep. So let’s look at how to leverage the builder pattern to achieve the same thing.
Builder Pattern Computer Building Class Example:
public class ComputerBuilderController(){
public ComputerCreator createMidTierComputer(){
return new ComputerCreator().
setCPU(midTierCPU).
setGPU(midTierGPU).
setMotherBoard(midTierMotherBoard).
setFan(midTierFan);
}
public void createTopTierComputer(){
return new ComputerCreator().
setCPU(topTierCPU).
setGPU(topTierGPU).
setMotherBoard(topTierMotherBoard).
setFan(topTierFan).
setNetworkCard(topTierNetworkCard);
}
}
public class ComputerCreator{
private CPU compCPU;
private GPU compGPU;
private MotherBoard compMotherBoard;
private Fan compFan;
private NetworkCard compNetworkCard;
private Speakers compSpeakers;
public ComputerCreator setCPU(CPU selectedCPU){
this.compCPU = selectedCPU;
return this;
}
public ComputerCreator setGPU(GPU selectedGPU){
this.compGPU = selectedGPU;
return this;
}
public ComputerCreator setMotherBoard(MotherBoard selectedMotherBoard){
this.compMotherBoard = selectedMotherBoard;
return this;
}
public ComputerCreator setFan(Fan selectedFan){
this.compFan = selectedFan;
return this;
}
public ComputerCreator setNetworkCard(NetworkCard selectedNetworkCard){
this.compNetworkCard = selectedNetworkCard;
return this;
}
public ComputerCreator setSpeakers(Speaker selectedSpeakers){
this.compSpeakers= selectedSpeakers;
return this;
}
}
You can see in the above example that using the builder pattern here gives us an enormous amount of flexibility. We no longer need to pass null values into a constructor or build a bajillion constructor variations, we only need to call the methods to set each piece of the computer should we need to set them. You can see we now only worry about setting values for things we actually need to set for our computer. Additionally, you can add new options for computer parts to set in the ComputerCreator class easily and it won’t affect that code that has already been written. For instance if I created a setWebcam method it would be no big deal. My createMidTierComputer and createTopTierComputer methods would not be impacted in any way and continue to function just fine. Builder Pattern FTW!
Why is it Useful?
Take the computer example above, without the builder pattern you get one of two things. You either get an enormous constructor you send all your computer parts to (that you will likely pass a ton of nulls to) or you have a ton of constructors to represent different computer variations… either choice is not a great choice. Complex objects typically have potentially hundreds of optional choices you can make, you need something more robust to select those options.
The builder pattern allows you to select those options piece by piece if you want them. Take for instance the computer example again. Desktop computers do not need things like network cards or speakers or a webcam to function. That being said, many people building a computer may want them for one reason or another to make their specific computer useful for them. Instead making constructor variations for every combination of those items, why not just use the builder pattern to add them as needed? It makes the code a whole lot easier to deal with and easier to extend in the future.
Where does it fit into Separation of Concerns?
Builder classes are typically service classes of some sort, maybe you create some super elaborate Opportunities in your org. You might have an Opportunity_Builder_Service or something along those lines. It can help in a lot of areas to reduce your code in the long term and to increase your codes flexibility for allowing new options for the object you are building, and I think we all know (if you’ve been doing this long enough), businesses like to add and subtract things from the services they create on a whim.
Where is it used in the Apex Common Library?
This design pattern is leveraged heavily by the fflib_QueryFactory class in the Apex Common Library. It allows us to build complex SOQL queries by building them step by step.
Every Domain layer class you create for an object should at minimum have the following logic in it for it to work as expected.
//All domain classes should utilize inherited sharing so that the caller determines whether it //should operate in system context or not. The should
//also extend the fflib_SObjectDomain class
public inherited sharing class Cases extends fflib_SObjectDomain{
//The constructor should always accept a list of the SObject type we're creating the
//domain class for
//It should then pass this list to the fflib_SObjectDomain class's constructor which is
//what super(cases) does.
//This sets the records value in the fflib_SObjectDomain class which is very important
public Cases(List<Case> cases){
super(cases);
}
//The name of this inner class must always be Constructor to work appropriately. This acts
//as a way to use the concept of reflection when initializing
//this class, despite the fact apex still does not support it.
public class Constructor implements fflib_SObjectDomain.IConstructable {
public fflib_SObjectDomain construct(List<SObject> sObjectList) {
return new Cases(sObjectList);
}
}
}
To understand why the Constructor inner class is necessary in these classes check out the triggerHandler method in the fflib_SObjectDomain class here: fflib_SObjectDomain triggerHandler method
Trigger Implementations using the Apex Common Library’s Domain Layer
If you didn’t know already, triggers should ideally have no logic in them… ever. Thankfully this concept has also been built into the Apex Common Library. To call the Domain Layer class you have built for your object in your trigger, just do the following:
//Note that I like to use the _Trigger in my trigger names, this is just personal preference //as it makes it easier to discern it's a trigger
trigger NameOfDomainLayerClass_Trigger on YourObject (before insert, before update, after insert, after update)
{
//This trigger handler method eventually calls the Construct inner class of your Domain
//class to construct a version of your class
//and implement the logic in it
fflib_SObjectDomain.triggerHandler(NameOfDomainLayerClass.class);
}
How to Access the Trigger variables in your Domain Class
Technically, you could leverage trigger.new, trigger.oldMap etc in your domain class… however you shouldn’t for two reasons. The first reason is you will likely (at some point) want to call some aspects of your Domain class from outside a trigger context. If your Domain relies on the trigger context to operate, that’s less than ideal. The second reason is you can’t mock the trigger context, so a ton of benefit of setting up these separation of concerns will be lost. Short story, never access trigger context variables directly in your domain class.
Now you might be wondering, “This Domain class is supposed to be able to run in trigger context… I need to access those variables!!”. No worries, you can still access them when you need them. If you’ve worked in SF long enough, with time you start to learn the only trigger context variables you need access to are trigger.new and trigger.oldMap. The rest typically really shouldn’t be used. Trust me… you don’t need them.
So how do you actually get access to trigger.oldMap and trigger.new? Well that requires us to take a closer look at the triggerHandler method in the fflib_SObjectDomain class that our actual triggers call (example just above this section).
Basically when our trigger calls that triggerHandler method, it eventually runs the code below (source code here):
The code above essentially passes trigger.new to the Records variable in the fflib_SObjectDomain class you Domain class extends when you are doing and insert, update or undelete operation; and it passes in trigger.oldMap.values to the Records variable if you are doing a delete operation.
Ok that’s cool, but how do you access trigger.oldMap when you need it?? Well, the only time you need trigger.oldMap are in update operations, so that’s the only time it’s accessible. When you setup your onBeforeUpdate or onAfterUpdate methods in your Domain class you’ll set them up like what you see below:
public override void onBeforeUpdate(Map<Id, SObject> existingRecords){
//existingRecords is trigger.oldMap
}
In trigger context when onBeforeUpdate gets called, trigger.oldMap is passed in to the existingRecords variable and you’re free to use it as you please.
There you have it! That’s it! Simpler than you maybe thought… maybe, lol.
The fflib_SObject Domain Class methods Cheat Sheet
While there are many other accessible methods in the fflib_SObjectDomain class below are the methods most commonly utilized in implementations.
1) onApplyDefaults() – This method is called in the handleBeforeInsert method and exists so that you can apply default logic that is applicable to all new records that are created in the system. 2) onValidate() – This method is called in the handleAfterInsert method and exists so that you can apply validation logic to your inserted records before commiting them to the database. 3) onValidate(Map<Id, SObject> existingRecords) – This method is called in the handleAfterUpdate method and exists so that you can apply validation logic to your updated records before commiting them to the database. 4) onBeforeInsert() – This method is called in the handleBeforeInsert method and exists so that you can override it to place logic that should occur during a before insert action in a trigger. 5) onBeforeUpdate(Map<Id, SObject>) – This method is called in the handleBeforeUpdate method and exists so that you can override it to place logic that should occur during a before update action in a trigger. 6) onBeforeDelete() – This method is called in the handleBeforeDelete method and exists so that you can override it to place logic that should occur during a before delete action in a trigger. 7) onAfterInsert() – This method is called in the handleAfterInsert method and exists so that you can override it to place logic that should occur during an after insert action in a trigger. 8) onAfterUpdate(Map<Id, SObject>) – This method is called in the handleAfterUpdate method and exists so that you can override it to place logic that should occur during an after update action in a trigger. 9) onAfterDelete() – This method is called in the handleAfterDelete method and exists so that you can override it to place logic that should occur during an after delete action in a trigger. 10) onAfterUndelete() – This method is called in the handleAfterUndelete method and exists so that you can override it to place logic that should occur during an after undelete action in a trigger. 11) handleBeforeInsert() – This method is called in the triggerHandler method when a beforeInsert is happening in the trigger. By default it calls the onApplyDefaults method and the onBeforeInsert method but it can be overridden and implemented in a different way if desired. 12) handleBeforeUpdate(Map<Id, SObject>) – This method is called in the triggerHandler method when a beforeUpdate is happening in the trigger. By default it calls the onBeforeUpdate method but it can be overridden and implemented in a different way if desired. 13) handleBeforeDelete() – This method is called in the triggerHandler method when a beforeDelete is happening in the trigger. By default it calls the onBeforeDelete method but it can be overridden and implemented in a different way if desired. 14) handleAfterInsert() – This method is called in the triggerHandler method when an afterInsert is happening in the trigger. By default it calls the onValidate and onAfterInsert method but it can be overridden and implemented in a different way if desired. 15) handleAfterUpdate() – This method is called in the triggerHandler method when an afterUpdate is happening in the trigger. By default it calls the onValidate and onAfterUpdate method but it can be overridden and implemented in a different way if desired. 16) handleAfterDelete() – This method is called in the triggerHandler method when an afterDelete is happening in the trigger. By default it calls the onAfterDelete method but it can be overridden and implemented in a different way if desired. 17) handleAfterUndelete() – This method is called in the triggerHandler method when an afterUndelete is happening in the trigger. By default it calls the onUndelete method but it can be overridden and implemented in a different way if desired. 18) getChangedRecords(Set<String> fieldNames) – This method will return a list of records that have had their fields changed (the fields specificied in the method parameter passed in). 19) getChangedRecords(Set<Schema.SObjectField> fieldTokens) – This method will return a list of records that have had their fields changed (the fields specificied in the method parameter passed in). I would suggest using this method over the one above. Strongly typed field names are a better choice in my opinion so the system knows your code depends on that field.
The Configuration Inner Class for fflib_SObjectDomain (Setting trigger state and trigger security)
Inside the fflib_SObjectDomain class you’ll find an inner class called Configuration. This inner class allows you to enable and disable Trigger State as well as enable and disable CRUD security in your trigger. By default trigger state is disabled and CRUD security is enabled.
Trigger State
The trigger state parameter allows you to choose to use the same instance of your Domain class between the before and after portion of the trigger. It needs to be used carefully as this could cause trigger recursion if not implemented properly.
How to turn trigger state on and off using the Configuration inner class:
//Turn on
Configuration.enableTriggerState();
//Turn off
Configuration.disableTriggerState();
Enforcing CRUD
The enforcing trigger CRUD (Create, Read, Update, Delete) ensures that a users has the appropriate object CRUD permissions before performing any actual DML actions. By default in the fflib_SObjectDomain class this is enforced. Ideally you should leave this as enforced unless you have a really excellent business reason to not enforce it.
How to turn CRUD enforcement on and off using the Configuration inner class:
The Trigger Event Inner Class (Turning trigger events on and off)
Inside the fflib_SObjectDomain class is an inner class called TriggerEvent that allows you to turn on and off the various trigger events at will. By default all trigger events are turned on.
Example Code for shutting down and re-enabling a portion of a domain trigger
//Disables to before insert portion of the trigger
DomainClassName.getTriggerEvent(DomainClassName.class).disableBeforeInsert();
//Code to execute
//Enables the before insert portion of the trigger
DomainClassName.getTriggerEvent(DomainClassName.class).enableBeforeInsert();
The following is a list of trigger event methods a what they do:
In most coding languages you need to connect to the database, query for the data and then you create wrapper classes to represent each underlying table in your database(s) to allow you to define how that particular table (object) should behave. Salesforce, however, already does a lot of this for you, for instance there is no need to connect to a Database, declarative behavior for you tables (objects) are already represented and your tables (objects) already have wrapper classes pre-defined for them (Ex: Contact cont = new Contact()).
However the logic represented in a trigger is an exception to this rule. Apex triggers represent a unique scenario on the Salesforce platform, they are necessary for complex logic, but inherently they do not abide by any object oriented principles. You can’t create public methods in them, you can’t unit test them, you can’t re-use logic placed directly in a trigger anywhere else in your system, etc. Which is a massive detriment we need to overcome. That’s where the domain layer comes in to play.
The Domain Layer will allow you on an object by object basis have an object oriented approach to centralize your logic. Basically, logic specific to a single object will be located in one place and only one place by using the domain layer. This ensures your logic specific to a single object isn’t split into a ton of different places across your org.
When to make a new Domain Layer Class
Basically, at the very least, anytime you need to make a trigger on an object you should implement a Domain Class. However this is a bit generalized, sometimes you don’t actually need a trigger on an object, but you have object specific behavior that should be implemented in a Domain class. For instance, if you have an object that doesn’t need a trigger, but it has a very specific way it should have its tasks created, you should probably create a Domain Layer class for that object and put that task creation behavior there.
A domain layer class is essentially a mixture of a trigger handler class and a class that represents object specific behaviors.
Where should you leverage the domain layer in your code?
You should only ever call to the domain layer code from service class methods or from other domain class methods. Controller, Batch Classes, etc should never call out to the domain directly.
Domain Class Naming Conventions
Class Names – Domain classes should be named as the plural of whatever object you are creating a domain layer for. For instance if you were creating a domain layer class for the Case object, the class would be declared as follows: public inherited sharing class Cases. This indicates that the class should be bulkified and handles multiple records, not a single object record.
Class Constructor – The constructor of these classes should always accept a list of records. This list of records will be leveraged by all of the methods within the domain class. This will be further explained below.
Method Names – Method names for database transaction should use the onTransactionName naming convention (Example: onAfterInsert). If the method is not related to a database transaction it should descriptive to indicate what domain logic is being executed within it (Example: determineCaseStatus).
Parameter Names and Types – You do not typically need to pass anything into your domain layer methods. They should primarily operate on the list of records passed in the constructor in the majority of situations. However some behavior based (non-trigger invoked) methods may need other domain objects and/or units of work passed to them. This will be further explained in the sections below.
Domain Layer Best Practices
Trasnaction Management
In the event you are actually performing DML operations in your Domain class, you should either create a Unit of Work or have one passed into the method doing the DML to appropriately manage your transaction. In the event you are not wanting to leverage the unit of work pattern you should make sure to at the very least set your System.Savepoint savePoint = Database.setSavePoint(); prior to doing your DML statement and use a try catch block to rollback if the DML fails.
Implementing the Domain Layer
To find out how to implement the Domain Layer using Apex Common, continue reading here: Implementing the Domain Layer with the Apex Common Library. If you’re not interested in utilizing the Apex Common library for this layer you can implement really any trigger framework and the core of the domain layer will be covered.