Nimbus runs record-triggered, autolaunched and platform event flows and their subflows inside your local Apex tests, in the platform's order of execution, without a deployment. A flow fault reaches your Apex as the exception Salesforce throws.
Most Salesforce orgs have hundreds of flows. They handle record updates, call Apex actions, enforce business rules, and trigger on DML operations right alongside triggers. Until now there was no way to run them locally, so you deployed first and found out afterwards.
So you write Apex tests, deploy to a scratch org, and watch them fail because a record-triggered flow modified the record the test was asserting on. Or a test that passes in isolation fails in the full org because an autolaunched flow was never accounted for.
Nimbus parses the flow metadata from source (the .flow-meta.xml files in your SFDX project) and executes the flows as part of the same test run, with no org and no deployment. Flows fire when DML fires, as in production.
# Your project structure
force-app/main/default/
├── classes/
│ ├── AccountService.cls
│ └── AccountServiceTest.cls
└── flows/
├── Account_Before_Save.flow-meta.xml
└── Onboarding_Autolaunched.flow-meta.xml
# nimbus test reads both.
# Flows fire when DML fires.
# No separate setup required.
nimbus test "AccountServiceTest.*"
# ✓ AccountServiceTest.testCreateAccount 18ms
# (Account_Before_Save flow fired, set Industry)
# ✓ AccountServiceTest.testOnboarding 24ms
# (Onboarding_Autolaunched ran, returned status)Fire on insert, update or delete, before or after save. Nimbus fires them as Salesforce does: in the documented order of execution, alongside triggers, with full access to record data. A fault fails the DML with the DmlException Salesforce throws: FIELD_CUSTOM_VALIDATION_EXCEPTION for a Custom Error, CANNOT_EXECUTE_FLOW_TRIGGER for anything else.
Called programmatically from Apex, scheduled jobs, or other flows. Test them by calling Flow.Interview directly: pass input variables, run, assert on output variables. An unhandled fault makes start() throw System.FlowException.
Flows that call other flows. Nimbus resolves the full call graph locally; subflows execute in-process, with the same isolation as the parent flow.
Formula fields evaluated inside flow decision elements are computed locally. Nimbus evaluates the same formula syntax Salesforce uses.
A before-save flow sets Industry to "Technology" when an Account's BillingCountry is "DE". Without flow execution, your test inserts the Account and sees Industry = null. With Nimbus, the flow fires.
The test class needs no changes, annotations or configuration. If the flow is in the project and it triggers on the DML operation being performed, it fires.
@isTest
static void testIndustryDefaultFlow() {
// Flow: Account_Before_Save
// Condition: BillingCountry = 'DE'
// Action: Set Industry = 'Technology'
Account acc = new Account(
Name = 'Acme GmbH',
BillingCountry = 'DE'
);
insert acc;
// The flow fired during insert.
// No separate invocation needed.
Account result = [SELECT Industry FROM Account WHERE Id = :acc.Id];
System.assertEquals('Technology', result.Industry);
}Autolaunched flows are invoked with Flow.Interview, either directly in the test or from the Apex code under test. Pass input variables using the map constructor, call start(), then read output variables back.
Nimbus resolves the flow from your project's flows/ directory, executes all its elements locally (decisions, assignments, loops, subflow calls) and returns the output variables you defined.
@isTest
static void testOnboardingFlow() {
// Flow: Onboarding_Autolaunched
// Input: AccountId (Id), Plan (String)
// Output: Status (String), NextStepDate (Date)
Account acc = new Account(Name = 'Acme');
insert acc;
Map<String, Object> inputs = new Map<String, Object>{
'AccountId' => acc.Id,
'Plan' => 'Enterprise'
};
Flow.Interview flow = Flow.Interview.createInterview(
'Onboarding_Autolaunched', inputs
);
flow.start();
String status = (String) flow.getVariableValue('Status');
Date nextStep = (Date) flow.getVariableValue('NextStepDate');
System.assertEquals('Active', status);
System.assertNotEquals(null, nextStep);
}Nimbus follows Salesforce's documented order of execution. On insert, this is what fires, in order:
Record-triggered flows, autolaunched flows and subflows execute locally, in the documented order, against your seeded test data.