Skip to main content

Basic Features

Dynamic SOQL

SOQL.cls class provide methods that allow build SOQL clauses dynamically.

// SELECT Id FROM Account LIMIT 100
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.setLimit(100)
.toList();

Automatic binding

All variables used in WHERE condition are binded by default.

// SELECT Id, Name FROM Account WHERE Name = :v1
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.whereAre(SOQL.Filter.with(Account.Name).contains('Test'))
.toList();
// Binding Map
{
"v1" : "%Test%"
}

Control FLS

AccessLevel Class

Object permission and field-level security is controlled by the framework. Developer can change FLS settings match business requirements.

User mode

By default all queries are in AccessLevel.USER_MODE.

The object permissions, field-level security, and sharing rules of the current user are enforced.

System mode

Developer can change it by using .systemMode() which apply AccessLevel.SYSTEM_MODE.

The object and field-level permissions of the current user are ignored, and the record sharing rules are controlled by the sharingMode.

// SELECT Id FROM Account - skip FLS
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.systemMode()
.toList();

Control Sharings

Apex Sharing

Use the with sharing or without sharing keywords on a class to specify whether sharing rules must be enforced. Use the inherited sharing keyword on a class to run the class in the sharing mode of the class that called it.

with sharing

By default all queries will be executed with sharing, because of AccessLevel.USER_MODE which enforce sharing rules.

AccessLevel.USER_MODE enforce object permissions and field-level security as well.

Developer can skip FLS by adding .systemMode() and .withSharing().

// Query executed in without sharing
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.systemMode()
.withSharing()
.toList();

without sharing

Developer can control sharing rules by adding .systemMode() (record sharing rules are controlled by the sharingMode) and .withoutSharing().

// Query executed in with sharing
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.systemMode()
.withoutSharing()
.toList();

inherited sharing

Developer can control sharing rules by adding .systemMode() (record sharing rules are controlled by the sharingMode) by default it is inherited sharing.

// Query executed in inherited sharing
SOQL.of(Account.SObjectType).with(new List<SObjectField> {
Account.Id, Account.Name
})
.systemMode()
.toList();

Mocking

Mocking provides a way to substitute records from a Database with some prepared data. Data can be prepared in form of SObject records and lists in Apex code or Static Resource .csv file. Mocked queries won't make any SOQL's and simply return data set in method definition, mock will ignore all filters and relations, what is returned depends solely on data provided to the method. Mocking is working only during test execution. To mock SOQL query, use .mockId(id) method to make it identifiable. If you mark more than one query with the same ID, all marked queries will return the same data.

public with sharing class ExampleController {

public static List<Account> getPartnerAccounts(String accountName) {
return AccountSelector.query()
.with(Account.BillingCity)
.with(Account.BillingCountry)
.whereAre(SOQL.FilterGroup
.add(SOQL.Filter.with(Account.Name).contains(accountName))
.add(SOQL.Filter.recordType().equal('Partner'))
)
.mockId('ExampleController.getPartnerAccounts')
.toList();
}
}

Then in test simply pass data you want to get from Selector to SOQL.setMock(id, data) method. Acceptable formats are: List<SObject>, SObject, or String with name of static resource. Then during execution Selector will return desired data.

List of records

@IsTest
private class ExampleControllerTest {

@IsTest
static void getPartnerAccounts() {
List<Account> accounts = new List<Account>{
new Account(Name = 'MyAccount 1'),
new Account(Name = 'MyAccount 2')
};

SOQL.setMock('ExampleController.getPartnerAccounts', accounts);

// Test
List<Account> result = ExampleController.getAccounts('MyAccount');

Assert.areEqual(accounts, result);
}
}

Single record

@IsTest
private class ExampleControllerTest {

@IsTest
static void getPartnerAccount() {
SOQL.setMock('ExampleController.getPartnerAccount', new Account(Name = 'MyAccount 1'));

// Test
Account result = (Account) ExampleController.getAccounts('MyAccount');

Assert.areEqual('MyAccount 1', result.Name);
}
}

Static resource

@IsTest
private class ExampleControllerTest {

@IsTest
static void getPartnerAccounts() {
SOQL.setMock('ExampleController.getPartnerAccounts', 'ProjectAccounts');

// Test
List<Account> result = ExampleController.getAccounts('MyAccount');

Assert.areEqual(5, result.size());
}
}

Count Result

@IsTest
private class ExampleControllerTest {

@IsTest
static void getPartnerAccountsCount() {
SOQL.setCountMock('mockingQuery', 2);

Integer result = SOQL.of(Account.sObjectType).count().mockId('mockingQuery').toInteger();

Assert.areEqual(2, result);
}
}

Avoid duplicates

Generic SOQLs can be keep in selector class.

public inherited sharing class AccountSelector implements SOQL.Selector {

public static SOQL query() {
return SOQL.of(Account.SObjectType)
.with(new List<SObjectField>{
Account.Name,
Account.AccountNumber
})
.systemMode()
.withoutSharing();
}

public static SOQL byRecordType(String rtDevName) {
return query.with(new List<SObjectField>{
Account.BillingCity,
Account.BillingCountry
}).whereAre(SOQL.Filter.recordType().equal(rtDevName));
}
}

Default configuration

The selector class can provide default SOQL configuration like default fields, FLS settings, and sharing rules.

public inherited sharing class AccountSelector implements SOQL.Selector {

public static SOQL query() {
return SOQL.of(Account.SObjectType)
.with(new List<SObjectField>{ // default fields
Account.Id,
Account.Name
}).systemMode(); // default FLS mode
}
}