Note
Explore test examples in the examples directory
The Salesforce DX Core Library provides a unit testing utility to help with mocking and sand-boxing core components. This feature allows unit tests to execute without needing to make API calls to salesforce.com.
- Mocking Authorizations
- Mocking Config Files
- Using the Built-in Sinon Sandboxes
- Testing Expected Failures
- Testing Log Lines
This code shows how to mock authorization for a Salesforce scratch org.
import { strictEqual } from 'assert';
import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup';
import { AuthInfo } from '@salesforce/core';
describe('Mocking Auth data', () => {
const $$ = new TestContext();
it('example', async () => {
const testData = new MockTestOrgData();
await $$.stubAuths(testData);
const auth = await AuthInfo.create({ username: testData.username });
strictEqual(auth.getUsername(), testData.username);
});
});
After having a valid AuthInfo object, you can then create fake connections to a Salesforce.com scratch org. You can then write tests that can validate result responses for SOQL queries and REST endpoints.
import { AuthInfo, Connection, SfError } from '@salesforce/core';
import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup';
import { AnyJson, ensureJsonMap, JsonMap } from '@salesforce/ts-types';
import { ensureString } from '@salesforce/ts-types';
import { deepStrictEqual } from 'assert';
import { QueryResult } from '@jsforce/jsforce-node';
describe('Mocking a force server call', () => {
const $$ = new TestContext();
it('example', async () => {
const records: AnyJson = { records: ['123456', '234567'] };
const testData = new MockTestOrgData();
await $$.stubAuths(testData);
$$.fakeConnectionRequest = (request: AnyJson): Promise<AnyJson> => {
const _request = ensureJsonMap(request);
if (request && ensureString(_request.url).includes('Account')) {
return Promise.resolve(records);
} else {
return Promise.reject(new SfError(`Unexpected request: ${_request.url}`));
}
};
const connection = await Connection.create({
authInfo: await AuthInfo.create({ username: testData.username }),
});
const result = await connection.query('select Id From Account');
deepStrictEqual(result, records);
});
});
You can mock the contents of various config files.
import { strictEqual } from 'assert';
import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup';
import { StateAggregator, OrgConfigProperties } from '@salesforce/core';
describe('Mocking Aliases', () => {
const $$ = new TestContext();
it('example', async () => {
const testData = new MockTestOrgData();
await $$.stubAliases({ myAlias: testData.username });
const alias = (await StateAggregator.getInstance()).aliases.get(testData.username);
strictEqual(alias, 'myAlias');
});
});
describe('Mocking Config', () => {
it('example', async () => {
const testData = new MockTestOrgData();
await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username });
const { value } = (await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG);
strictEqual(value, testData.username);
});
});
describe('Mocking a project config file', () => {
it('stubs the project config file', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: {
packageDirectories: [
{
path: 'force-app',
default: true,
},
],
sourceApiVersion: '57.0',
},
});
});
});
describe('Mocking Arbitrary Config Files', () => {
it('example', async () => {
// MyConfigFile must extend the ConfigFile class in order for this to work properly.
// Examples include: DeployCache, DeployPipelineCache, ScratchOrgCache
$$.setConfigStubContents('MyConfigFile', { contents: { foo: 'bar' } });
});
});
sfdx-core uses Sinon as its underlying mocking system. Find more information about Sinon and its sandboxing concept here.
Sinon stub
s and spy
s must be cleaned up after test invocations. To ease the use of Sinon with sfdx-core, we've exposed our sandbox in TestSetup
. After adding your own stub
s or spy
s, they'll automatically be cleaned up after each test using mocha's afterEach
method.
import { strictEqual } from 'assert';
import { TestContext } from '@salesforce/core/lib/testSetup';
import * as os from 'os';
describe('Using the built in Sinon sandbox.', () => {
const $$ = new TestContext();
it('example', async () => {
const unsupportedOS = 'LEO';
$$.SANDBOX.stub(os, 'platform').returns(unsupportedOS);
strictEqual(os.platform(), unsupportedOS);
});
});
It's important to have negative tests that ensure proper error handling. With shouldThrow
it's easy to test for expected async rejections.
import { SfError } from '@salesforce/core';
import { shouldThrow } from '@salesforce/core/lib/testSetup';
import { strictEqual } from 'assert';
class TestObject {
public static async method() {
throw new SfError('Error', 'ExpectedError');
}
}
describe('Testing for expected errors', () => {
it('example', async () => {
try {
await shouldThrow(TestObject.method());
} catch (e) {
strictEqual(e.name, 'ExpectedError');
}
});
});
You can also use shouldThrowSync
for syncrhonous functions you expect to fail.
import { SfError } from '@salesforce/core';
import { shouldThrowSync } from '@salesforce/core/lib/testSetup';
import { strictEqual } from 'assert';
class TestObject {
public static method() {
throw new SfError('Error', 'ExpectedError');
}
}
describe('Testing for expected errors', () => {
it('example', async () => {
try {
shouldThrowSync(() => TestObject.method());
} catch (e) {
strictEqual(e.name, 'ExpectedError');
}
});
});
It's also useful to check expected values and content from log lines. TestSetup configures the sfdx-core logger to use an in memory LogLine storage structure. These can be easily accessed from tests.
import { Logger, LogLine } from '@salesforce/core';
import { TestContext } from '@salesforce/core/lib/testSetup';
import { strictEqual } from 'assert';
const TEST_STRING = 'foo was here';
class TestObject {
constructor(private logger: Logger) {
this.logger = logger.child('TestObject');
}
public method() {
this.logger.error(TEST_STRING);
}
}
describe('Testing log lines', () => {
const $$ = new TestContext();
it('example', async () => {
const obj = new TestObject($$.TEST_LOGGER);
obj.method();
const records = $$.TEST_LOGGER.getBufferedRecords();
strictEqual(records.length, 1);
strictEqual(records[0].msg, TEST_STRING);
});
});