v5.0.0
08-April-2025
Scope
- Documentation Update
SOQL and SOQL Cache
- Better mocking with
Mockableinterface - Code performance improvement
- Better encapsulation
- Code refactoring
Documentation Update
We significantly improved SOQL Lib documentation: https://soql.beyondthecloud.dev/
You can find many use cases at https://soql.beyondthecloud.dev/examples/showcase.
The concept is straightforward—each example is shown in SOQL and its equivalent in SOQL Lib.
Example
SOQL
SELECT Id, Name, BillingCity
FROM AccountSOQL Lib
SOQL.of(Account.SObject)
.with(Account.Id, Account.Name, Account.BillingCity)
.toList();Better Mocking
We introduced a new Mockable interface. This interface lays the foundation for future improvements in mocking within SOQL Lib.
public interface Mockable {
// SObject
Mockable thenReturn(SObject record);
Mockable thenReturn(List<SObject> records);
// Count
Mockable thenReturn(Integer count);
}List<Account> accounts = new List<Account>{
new Account(Name = 'MyAccount 1'),
new Account(Name = 'MyAccount 2')
};
SOQL.mock('ExampleController.getPartnerAccounts')
.thenReturn(accounts);Stripping Extra Fields
We also introduced logic to strip unused fields during mocking.
Problem:
Previously, mocking returned all fields in the mock object, even if those fields weren't part of the actual query. This caused unit tests to pass, but led to runtime errors like:
System.SObjectException: SObject row was retrieved via SOQL without querying the requested field.
Solution:
We now keep only the fields that are actually queried. Extra fields are stripped from mocked records. Currently, this applies only to plain fields like Id, Name, etc. Support for relationship fields and subqueries is planned for future release.
for (Account account : SOQL.of(Account.SObjectType).with(Account.Name).toList()) {
String industry = account.Industry; // <- field not queried
}List<Account> accounts = new List<Account>{
new Account(Name = 'MyAccount 1', Industry = 'IT'),
new Account(Name = 'MyAccount 2', Industry = 'Administration')
};
SOQL.mock('ExampleController.getPartnerAccounts')
.thenReturn(accounts);Better Encapsulation
Previously, the method returned an instance of SOQL instead of the Queryable interface.
New Behavior:
This ensures that callers can only access the interface’s methods - supporting loose coupling, better encapsulation, and adherence to the Dependency Inversion Principle.
// ❌ Before
public SOQL with(SObjectField field) {
// ...
}
// ✅ After
public Queryable with(SObjectField field) {
// ...
}Code Refactoring
SObjectField Optimization
Instead of using:
field.getDescribe().getName()We now use:
field.toString()This change makes operations with SObjectField approximately 9x faster.
General Refactoring
We’ve streamlined the code in the SOQL class, resulting in a shorter and more maintainable implementation.
🚨 Breaking Changes 🚨
Mocking
Old mocking methods are still supported for backward compatibility. However, we recommend using the new approach with SOQL.mock(...).thenReturn(...);.
// Backward support
@TestVisible // deprecated
private static void setMock(String mockId, SObject record) {
mock(mockId).thenReturn(record);
}
@TestVisible // deprecated
private static void setMock(String mockId, List<SObject> records) {
mock(mockId).thenReturn(records);
}
@TestVisible // deprecated
private static void setCountMock(String mockId, Integer amount) {
mock(mockId).thenReturn(amount);
}Queryable
As mentioned in the Better Encapsulation section, all SOQL methods now return the Queryable interface instead of the SOQL class directly.
// ❌ Before
public SOQL with(SObjectField field) {
// ...
}
// ✅ After
public Queryable with(SObjectField field) {
// ...
}If you have existing logic like this:
SOQL query = SOQL.of(...);Replace it with:
SOQL.Queryable query = SOQL.of(...);