Fast Start - Test Data
Framework: KernDX | Total time: ~30 minutes
> Create test records with automatic field population, bulk creation, parent-child relationships, > and DML-free query mocking -- override only what your test cares about.
Before you start:
- [ ] KernDX package installed in your org
- [ ] Org configured post-install — verify with the Kern app's Health Check (see Installation guide)
- [ ] CLI authenticated (
sf org open -o YourOrgAliasto verify) — or just use the Developer Console (Gear Icon > Developer Console) for all Apex work - [ ] Working in a sandbox or scratch org (not production)
What you'll build: A test class that uses TST_Builder for clean test data creation and TST_Mock for DML-free query testing -- with 100% coverage on a simple utility class.
Success looks like: Your tests create Accounts with auto-populated fields, bulk cycling values, and DML-free mocks -- all without hardcoding boilerplate setup code.
In one line: kern.TST_Builder.of(Account.SObjectType).withOverride(Account.Industry, 'Technology').build(); -- auto-populates required fields, inserts, and returns the record in one expression.
Table of Contents
Expand
Tier 1: See It Work (~2 minutes)
Use TST_Builder directly from anonymous Apex. No custom classes needed.
Create a Record with Auto-Populated Fields
Open Developer Console > Debug > Open Execute Anonymous Window and run:
Account newAccount = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Phone, '555-0100').build();
System.debug('Name: ' + newAccount.Name);
System.debug('Id: ' + newAccount.Id);Expected output (name is randomly generated):
Name: cqpcCtfZke0
Id: 001...TST_Builder populates Salesforce-required fields automatically. Custom validation rules may require additional .withOverride() calls.
Override Specific Fields
Account newAccount = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Name, 'Acme Corp')
.withOverride(Account.Industry, 'Technology')
.withOverride(Account.Phone, '555-0100')
.withOverride(Account.Website, 'https://example.com')
.build();
System.debug('Name: ' + newAccount.Name + ', Industry: ' + newAccount.Industry);Expected output:
Name: Acme Corp, Industry: TechnologyBulk Creation
Create multiple records with a single DML statement:
List<SObject> accounts = kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Phone, '555-0100')
.withCount(3)
.buildList();
System.debug('Created ' + accounts.size() + ' accounts');In-Memory Records (No DML)
.withoutInsertion() = in-memory only (no Id). .withoutInsertion(true) = in-memory with mock Id (for code that checks record.Id != null).
Account inMemory = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Name, 'In-Memory Co')
.withoutInsertion()
.build();
System.debug('Id: ' + inMemory.Id + ', Name: ' + inMemory.Name);Expected output:
Id: null, Name: In-Memory Co> When to move to Tier 2: When you want to use TST_Builder in real test classes with parent-child > relationships, cycling values, and query mocking.
Tier 2: Build Your Own (~20 minutes)
> No local project? You can create classes directly in the Developer Console (Gear Icon > Developer > Console > File > New > Apex Class) and run tests from there too (Test > New Run). Paste the code, save, > and skip the sf project deploy start and sf apex run test commands.
Build a utility class that counts Accounts by industry, then write tests using TST_Builder and TST_Mock.
Step 1: Create the Utility Class
Create a new file named FastStart_TestData_DEMO.cls:
/**
* @description Counts Account records by Industry.
*
* @see FastStart_TestData_DEMO_TEST
*
* @author your.name@company.com
*
* @group Utilities
*
* @date May 2026
*/
public with sharing class FastStart_TestData_DEMO
{
/**
* @description Returns the number of Accounts whose Industry matches the given value.
*
* @param industry The Industry picklist value to filter on.
*
* @return The count of matching Account records.
*/
public static Integer countAccountsByIndustry(String industry)
{
return kern.QRY_Builder.selectFrom(Account.SObjectType)
.condition(Account.Industry).equals(industry)
.count();
}
}Deploy:
sf project deploy start -o YourOrgAlias -m "ApexClass:FastStart_TestData_DEMO"Step 2: Create the Test Class
Create a new file named FastStart_TestData_DEMO_TEST.cls:
/**
* @description Unit tests for FastStart_TestData_DEMO.
* Demonstrates TST_Builder (real DML), TST_Mock (DML-free), and cycling values.
*
* @see FastStart_TestData_DEMO
*
* @author your.name@company.com
*
* @group Utilities
*
* @date May 2026
*/
@SuppressWarnings('PMD.ApexUnitTestClassShouldHaveRunAs')
@IsTest(SeeAllData=false IsParallel=true)
private class FastStart_TestData_DEMO_TEST
{
/** @description Industry value used in insert-based tests. */
private static final String INDUSTRY_TECH = 'Technology';
/** @description Industry value used in mock-based tests. */
private static final String INDUSTRY_FINANCE = 'Finance';
/** @description Industry that should yield no results. */
private static final String INDUSTRY_OTHER = 'Education';
/** @description Verifies that 5 inserted accounts are counted correctly. */
@IsTest
private static void shouldCountRealInsertedAccounts()
{
kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Industry, INDUSTRY_TECH)
.withCount(5)
.buildList();
Integer result = FastStart_TestData_DEMO.countAccountsByIndustry(INDUSTRY_TECH);
Assert.areEqual(5, result, 'Count should match the 5 inserted Technology accounts');
}
/** @description Verifies cycling values produce the expected mix of industries. */
@IsTest
private static void shouldBuildListWithCyclingIndustryValues()
{
List<SObject> records = kern.TST_Builder.of(Account.SObjectType)
.withCycle(Account.Industry, new List<Object>{ INDUSTRY_TECH, INDUSTRY_FINANCE })
.withCount(4)
.buildList();
Assert.areEqual(4, records.size(), 'buildList should return 4 records');
Assert.areEqual(2, FastStart_TestData_DEMO.countAccountsByIndustry(INDUSTRY_TECH), 'Should have 2 Technology accounts');
Assert.areEqual(2, FastStart_TestData_DEMO.countAccountsByIndustry(INDUSTRY_FINANCE), 'Should have 2 Finance accounts');
}
/** @description Verifies TST_Mock registers a record without DML so QRY_Builder returns it. */
@IsTest
private static void shouldCountMockedAccountWithoutDml()
{
kern.TST_Mock.of(Account.SObjectType)
.withOverride(Account.Industry, INDUSTRY_FINANCE)
.build();
Integer result = FastStart_TestData_DEMO.countAccountsByIndustry(INDUSTRY_FINANCE);
Assert.areEqual(1, result, 'Mock-registered Finance account should be returned by QRY_Builder count');
}
/** @description Verifies an in-memory record does not affect the database count. */
@IsTest
private static void shouldReturnZeroWhenNoMatchingAccounts()
{
kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Industry, INDUSTRY_TECH)
.withoutInsertion()
.build();
Integer result = FastStart_TestData_DEMO.countAccountsByIndustry(INDUSTRY_OTHER);
Assert.areEqual(0, result, 'In-memory record should not affect the database count for a different industry');
}
}Deploy and run:
sf project deploy start -o YourOrgAlias -m "ApexClass:FastStart_TestData_DEMO" -m "ApexClass:FastStart_TestData_DEMO_TEST"
sf apex run test -o YourOrgAlias -t FastStart_TestData_DEMO_TEST --code-coverage --synchronous --result-format humanExpected: 4 tests passing, 100% coverage on FastStart_TestData_DEMO.
Why it works -- key patterns:
TST_Builder.of(SObjectType)-- Creates a builder for any SObject type. Auto-populates required fields..withOverride(field, value)-- Sets specific field values. Override only what matters for the test..withoutInsertion()-- Builds in-memory (no DML). Use when testing logic that doesn't need a real record..withCycle(field, values)-- Rotates values across records inbuildList(). One call covers multiple scenarios..withCount(n).buildList()-- Bulk creation with a single DML statement.TST_Mock.of(SObjectType).build()-- Creates a record without DML and auto-registers it so thatQRY_Builderqueries return mock data instead of hitting the database.
> About the annotations: @IsTest(SeeAllData=false IsParallel=true) is the standard declaration for > classes that don't insert User / Group / PermissionSet records. @SuppressWarnings('PMD.ApexUnitTestClassShouldHaveRunAs') > suppresses a static analysis rule about System.runAs() -- fine for quick starts, but consider adding > System.runAs() in production tests to verify profile and permission set access. Remove IsParallel=true > if your test class inserts User records (Salesforce forbids User DML in parallel tests).
Tier 3: Production Patterns (~5 minutes)
Multiple Field Overrides
Use a map when overriding many fields:
Account record = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverrides(new Map<SObjectField, Object>
{
Account.Name => 'Acme Corp',
Account.Industry => 'Technology',
Account.AnnualRevenue => 5000000,
Account.Phone => '555-0100',
Account.Website => 'https://example.com'
})
.build();Mock IDs (No DML)
Generate fake IDs for records that need an ID but don't need database insertion:
Account mockAccount = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Name, 'Mock Corp')
.withoutInsertion(true)
.build();
System.debug('Mock Id: ' + mockAccount.Id); // e.g., 001000000000001AAAUse .withoutInsertion(true) when you need an ID for logic that checks record.Id != null but don't want to hit the database.
TST_Mock for DML-Free Query Interception
TST_Mock creates mock records and automatically registers them so that QRY_Builder queries return mock data instead of hitting the database.
> @IsTest context only: TST_Mock works exclusively inside test classes. The examples below show > how to use it in test methods -- they cannot be run from Execute Anonymous.
/** @description Tests that the classifier handles mock data without DML. */
@IsTest
private static void shouldClassifyMockedAccount()
{
kern.TST_Mock.of(Account.SObjectType)
.withOverride(Account.Name, 'Mock Corp')
.withOverride(Account.Industry, 'Technology')
.build();
Account result = (Account)kern.QRY_Builder.selectFrom(Account.SObjectType)
.fields(new List<SObjectField>{ Account.Name, Account.Industry })
.getFirst();
Assert.areEqual('Mock Corp', result.Name, 'Should return mock record');
Assert.areEqual('Technology', result.Industry, 'Should have mocked Industry');
}Multiple mocks with bulk cycling:
@IsTest
private static void shouldReturnBulkMocks()
{
kern.TST_Mock.of(Account.SObjectType)
.withCycle(Account.Industry, new List<Object>{ 'Technology', 'Finance' })
.withCount(4)
.buildList();
List<Account> results = kern.QRY_Builder.selectFrom(Account.SObjectType)
.fields(new List<SObjectField>{ Account.Name, Account.Industry })
.toList();
Assert.areEqual(4, results.size(), 'Should return 4 mock records');
}Clear mocks when needed (Salesforce resets static state between test methods automatically):
kern.TST_Mock.clear(); // Clear all types
kern.TST_Mock.clear(Account.SObjectType); // Clear one typeNegative-Path: Simulate a SOQL Failure
kern.TST_Mock.throwsException(SObjectType[, Exception/String]) registers an exception that fires the next time the framework queries the specified type. Use it to exercise catch blocks around a SOQL call without having to mock at a different layer.
/** @description Verifies the caller surfaces a friendly error when the underlying SOQL fails. */
@IsTest
private static void shouldHandleQueryFailureGracefully()
{
Id accountId = kern.UTIL_Random.randomId(Account.SObjectType);
kern.TST_Mock.throwsException(Account.SObjectType, new QueryException('Simulated SOQL failure'));
try
{
new SEL_Accounts().findById(accountId);
Assert.fail('Expected the SEL_Accounts query to throw');
}
catch(QueryException error)
{
Assert.areEqual('Simulated SOQL failure', error.getMessage(), 'Should rethrow the simulated failure');
}
}Three overloads are available; pick the shortest that names the failure:
// Throw a custom exception (most explicit)
kern.TST_Mock.throwsException(Account.SObjectType, new QueryException('Simulated failure'));
// Throw a QueryException with a message (most common)
kern.TST_Mock.throwsException(Account.SObjectType, 'Simulated failure');
// Throw a generic QueryException naming the SObjectType (quickest)
kern.TST_Mock.throwsException(Account.SObjectType);Coexistence with record mocks: if both an exception and records are registered for the same SObjectType, the exception is thrown first -- the record path is unreachable. Both registrations are wiped by kern.TST_Mock.clear() and kern.TST_Mock.clear(SObjectType).
TST_Factory for Users
Create test users with specific profiles. Remove IsParallel=true from the test class header when inserting User records -- Salesforce forbids User DML in parallel tests.
/** @description Tests behaviour as a Standard User. */
@IsTest
private static void shouldRunAsStandardUser()
{
User testUser = kern.TST_Factory.newUser('Standard User');
kern.DML_Builder.newTransaction().doInsert(testUser).execute();
System.runAs(testUser)
{
Account record = (Account)kern.TST_Builder.of(Account.SObjectType)
.withOverride(Account.Industry, 'Technology')
.withoutInsertion()
.build();
Assert.areEqual('Technology', record.Industry, 'Industry should be set');
}
}TST_Factory for Feature Flags
Create and activate a feature flag for testing flag-gated code paths:
kern.TST_Factory.newFeatureFlag('MyFeatureFlag');
Boolean isEnabled = kern.UTIL_FeatureFlag.isEnabled('MyFeatureFlag');
Assert.isTrue(isEnabled, 'Flag should be enabled');Framework Metadata: Trigger and Validation Setup
To exercise framework Trigger Action or Validation Rule behaviour in your tests -- or to set up that metadata from a setup script -- deploy the relevant CustomMetadata records via XML. See Fast Start - Trigger Actions and Fast Start - Custom Validations for the XML deploy pattern.
Once the metadata is in place, the action or validation fires through the normal trigger path, so your test simply builds a record and asserts the resulting behaviour:
@IsTest
private static void shouldFireTriggerAction()
{
Account record = (Account)kern.TST_Builder.of(Account.SObjectType).build();
Assert.isNotNull(record.Id, 'Record should be inserted and the trigger action should have fired');
}Common Issues
| Problem | Cause | Fix |
|---|---|---|
SObject type mismatch | Forgot to cast .build() result | Cast: (Account)kern.TST_Builder.of(Account.SObjectType).build() |
REQUIRED_FIELD_MISSING on insert | Field marked required but builder doesn't know about it | Add .withOverride(field, value) for the missing field |
| Records created in a loop | Using .build() inside a loop | Use .withCount(n).buildList() for bulk creation (single DML) |
| Mock IDs when not needed | Using .withoutInsertion(true) for simple in-memory tests | Use .withoutInsertion() (no argument) when IDs aren't needed |
| Mocks leaking between tests | Forgetting to clear TST_Mock state | Salesforce resets static state between tests -- no manual cleanup needed |
null for optional fields | Builder only auto-populates required fields | Use .withOverride() to set optional fields your test needs |
| Trigger action or validation rule doesn't fire in a test | The framework metadata isn't present in the org | Deploy the relevant CustomMetadata records via XML for triggers and validations |
What You Now Know
After completing this guide, you understand the three test data tools in KernDX:
| Tool | When to Use | What You Get |
|---|---|---|
TST_Builder | All SObject construction in tests | Auto-populated fields, bulk, cycling, parent-child, real DML |
TST_Mock | DML-free query interception in @IsTest | Mock records auto-registered for QRY_Builder without touching the database |
TST_Factory | Users and feature flags | Test users and feature flags ready for your tests |
Key patterns:
- Override only what matters -- let
TST_Builderhandle required fields. Tests stay focused on behaviour. .withoutInsertion()for logic tests -- skip DML when testing pure logic (faster tests, no governor cost).withCycle()for multiple scenarios -- test different values without writing separate test methods.withChildren()for relationships -- auto-handles foreign key assignmentTST_Mockfor query mocking -- no DML, no database, mock data flows throughQRY_Builder- Framework metadata via XML -- to exercise trigger or validation behaviour in your tests, deploy the relevant
CustomMetadatarecords via XML
Next Steps
| Topic | Link |
|---|---|
| Selectors (query patterns) | Fast Start - Selectors |
| DML Builder (writes) | Fast Start - DML |
| Trigger Actions (testing triggers) | Fast Start - Trigger Actions |
| Custom Validations | Fast Start - Custom Validations |
| E2E Testing (Playwright + RunLocalTests harness) | Fast Start - E2E Testing |
| TST_Builder Reference | reference/apex/TST_Builder.md |
| TST_Mock Reference | reference/apex/TST_Mock.md |
| TST_Factory Reference | reference/apex/TST_Factory.md |