Salesforce SOQL-Free Apex Tests with Selector Pattern
With Apex a Selector Pattern provides multiple benefits including
- A centralized place to query once, minimizing the number of queries
- The ability to test the queries in the Selector independent from the classes that utilize the Selector
Selector Patterns can allow for a configurable query. In the case below we configure with Custom Metadata
public class AccountSelector implements IAccountSelector {
public List<Account> selectById(List<Id> accountIds){
list<string> fieldList = new list<string>{ 'Id' };
for (Account_Selector_Field__mdt asf : Account_Selector_Field__mdt.getAll().values())
fieldList.add(asf.fieldname__c);
String accountQuery = 'SELECT {0} FROM Account WHERE Id in (\'{1}\')'
.replace('{0}',String.join(fieldList,','))
.replace('{1}',String.join(accountIds,'\',\''));
return Database.query(accountQuery);
}
}
As indicated create an interface
public interface IAccountSelector {
List<Account> selectById(List<Id> accountIds);
}
The below trigger handler class does not do anything functional but it is an example of how we can inject a Mock of the selector into the constructor. This allows query-free dml-free tests on the class.
public class AccountTriggerHandler {
IAccountSelector accountSelector;
public AccountTriggerHandler(){
accountSelector = new AccountSelector();
}
public AccountTriggerHandler(IAccountSelector anAccountSelector{
accountSelector = anAccountSelector;
}
public List<Account> getAccounts(Id accountId){
return accountSelector.selectById(new List<Id>{ accountId});
}
}
In the test, we can Mock the selection of data by creating a Mock class and generating the data we care about in the Mock.
@isTest
private class AccountTriggerHandlerTest{
static testMethod void testGetAccounts() {
AccountTriggerHandler ath = new AccountTriggerHandler(new AccountSelectorTest());
List<Account> testResults = ath.getAccounts(’0011k0000000000AAA’);
System.assertEquals(testResults[0].Id, '0011k0000000000AAA’);
}
class AccountSelectorTest implements IAccountSelector {
public List<Account> selectById(List<Id> accountIds){
return (List<Account>) JSON.deserialize(
'[{"Id":"0011k0000000000AAA","Name":"testAccount"}]'
, List<Account>.class
);
}
}
}
The test injects a selector mock now allowing for SOQL-free apex tests. The Selector pattern limits SOQL to tests that test the selector patterns, allowing us to scale with tests that don’t need awkward workaround to queries.