-
Notifications
You must be signed in to change notification settings - Fork 0
Mocking DML Operations
Using DatabaseLayer.Dml, you can easily engage mocks to test your code without directly interacting with the Salesforce database.
You can cover most test cases by adding just a single line of code to your tests:
First, ensure all DML statements in the code path to be tested use DatabaseLayer.Dml methods. Standard DML operations cannot be mocked.
In @IsTest context, call DatabaseLayer.useMocks() or DatabaseLayer.useMockDml(). Now, any Dml operations will be processed by the MockDml instead.
By default, MockDml will simulate a successful DML operation. You can optionally inject errors using the MockDml.shouldFail or MockDml.shouldFailIf methods.
Finally, call the code you want to test. As it runs, the framework will run MockDml logic instead of actually calling the Salesforce database.
By default, MockDml operations will simulate a successful DML operation. Successful DML operations result in the following:
- Inserted (or upserted records without an Id) will be assigned a unique record Id which corresponds with its
SObjectType. - The resulting database result object(s) (ex.,
Database.SaveResult) will haveisSuccessset to true. - Since DML isn't actually being processed, you won't be able to retrieve the records from the Salesforce database via SOQL. Instead, leverage
MockDml's own mock database. This mock database stores all records that were operated on, for each DML operation. You can access these through theMockDml's properties, ex.MockDml.INSERTED.
Example:
DatabaseLayer.useMocks();
Account account = new Account();
// By default, DML operations using mocks will succeed:
Database.SaveResult result = DatabaseLayer.Dml.doInsert(account);
Assert.isTrue(result?.isSuccess(), 'Insert failed');
Assert.isNotNull(account?.Id, 'Missing Id');
Assert.areEqual(true, MockDml.INSERTED.wasProcessed(account), 'Was not processed');If you want all DML operations to fail, use the shouldFail method.
DatabaseLayer.useMocks();
MockDml.shouldFail();
// Now all DML operations will fail:
Account account = new Account();
DatabaseLayer.Dml.doInsert(account);
// ! System.DmlExceptionIf you want more advanced logic, or if you want to return a custom error, use the shouldFailIf method. This method accepts a MockDml.ConditionalFailure object, which returns an Exception to be thrown if certain conditions are met.
In this example, the insert succeeds, but the subsequent update fails:
DatabaseLayer.useMocks();
// See this class's logic below for more details:
MockDml.ConditionalFailure logic = new FailOnUpdate();
MockDml.shouldFailIf(logic);
// Now all DML "update" operations will fail:
Account account = new Account();
DatabaseLayer.Dml.doInsert(account);
account.OwnerId = UserInfo.getUserId();
DatabaseLayer.Dml.doUpdate(account);
// ! System.DmlExceptionpublic class FailOnUpdate implements MockDml.ConditionalFailure {
public Exception checkFailure(Dml.Operation operation, SObject record) {
if (operation == MockDml.DO_UPDATE) {
// The operation should fail:
return new System.DmlException();
} else {
// The operation should succeed:
return null;
}
}
}All mocked failures respect the mode of failure, as defined by the DML method's allOrNone flag:
DatabaseLayer.useMocks();
// All DML operations will fail, according to its AllOrNone value
MockDml.shouldFail();
Account account = new Account();
// AllOrNone = false, no error is thrown but the database result does not succeed:
Database.SaveResult result = DatabaseLayer.Dml.doInsert(account, false);
System.debug(result?.isSuccess()); // > "false"
// AllOrNone = true, an exception is thrown:
DatabaseLayer.Dml.doInsert(account, true);
// ! System.DmlExceptionThe framework also allows developers to simulate System.Savepoint objects and their associated operations. Using the MockDml.SavepointHistory and MockDml.Savepoint objects, developers can inspect how savepoints were used over the course of an apex test.
DatabaseLayer.useMocks();
Account account = new Account();
Test.startTest();
System.Savepoint sp = DatabaseLayer.Dml.setSavepoint();
DatabaseLayer.Dml.doInsert(account);
Database.rollback(sp);
Test.stopTest();
MockDml.Savepoint savepoint = MockDml.SAVEPOINTS?.get(0);
Assert.isTrue(savepoint?.wasRolledBack, 'Savepoint was not rolled back');
// Because the savepoint was rolled back, the Account wasn't actually inserted:
Assert.isTrue(MockDml.INSERTED?.getAll()?.isEmpty(), 'Records were inserted');Note: By default, when a savepoint is rolled back, the
MockDml's database is reset to the point in time that the savepoint was generated. This behavior can be overridden, by settingMockDml.MockDatabase.resetOnRollback = false.
- Generating Test Records
- Dml
- Soql
- Cmdt
- Plugins
- DatabaseLayer
- Dml
- MockDml
- MockRecord
- Cmdt
- MockCmdt
- MockSoql
-
Soql
- Soql.AggregateResult
- Soql.Aggregation
- Soql.Binder
- Soql.Builder
- Soql.Condition
- Soql.ConditionalLogic
- Soql.Criteria
- Soql.Cursor
- Soql.Function
- Soql.InnerQuery
- Soql.InvalidParameterValueException
- Soql.LogicType
- Soql.NullOrder
- Soql.Operation
- Soql.Operator
- Soql.ParentField
- Soql.PreAndPostProcessor
- Soql.QueryLocator
- Soql.Request
- Soql.Scope
- Soql.Selectable
- Soql.SortDirection
- Soql.SortOrder
- Soql.Subquery
- Soql.TypeOf
- Soql.Usage
- Soql.WhenClause