Testing
Workflow::fake() swaps the execution manager for a recording wrapper. Workflows
still run for real — through the real engine, nodes, and resume logic — but in
Test mode: waits don't sleep, child workflows resolve inline, nothing touches a
real queue. Execution records are kept in memory so you can assert on them.
use Qanna\WorkflowEngine\Facades\Workflow;
public function test_signup_sends_a_welcome_email(): void
{
Workflow::fake();
// ... arrange: the workflow definition still needs to exist in storage ...
$execution = Workflow::run('send-welcome-email', ['email' => 'ada@example.com']);
Workflow::assertRan('send-welcome-email');
Workflow::assertCompleted($execution->id);
}
Workflow::fake()only fakes execution storage. Workflow definitions are still read from the configured driver, so persist them in your test setup (or point definition storage at a temp path / memory).
Assertions
| Assertion | Passes when |
|---|---|
assertRan($workflowId) | The workflow ran at least once via run(). |
assertNotRan($workflowId) | It never ran. |
assertNothingRan() | No workflow ran. |
assertDispatched($workflowId) | It was started via dispatch(). |
assertResumed($executionId) | That execution was resumed. |
assertWorkflowRanTimes($workflowId, $n) | It ran exactly $n times. |
assertExecutionCount($n) | $n distinct executions were recorded. |
assertCompleted($executionId) | Final status Succeeded. |
assertSuspended($executionId) | Final status Suspended. |
assertFailed($executionId) | Final status Failed. |
assertExecutionStatus($executionId, ExecutionStatus::…) | Exact status. |
assertExecutionOutput($executionId, $expected) | Output identical to $expected. |
Accessors
$fake = Workflow::fake();
// ... run some workflows ...
$fake->runs(); // WorkflowExecution[] — one per run() call
$fake->dispatches(); // one per dispatch() call
$fake->resumes(); // one per resume() call
$fake->executions(); // deduped by execution id, latest state
$fake->findExecution($executionId);
Testing suspend / resume
Because child workflows and waits resolve inline in test mode, a full suspend → resume cycle runs in one test:
Workflow::fake();
$suspended = Workflow::run('needs-approval');
Workflow::assertSuspended($suspended->id);
$resumed = Workflow::resume($suspended->id, ['approved' => true]);
Workflow::assertCompleted($suspended->id);
$this->assertSame(['approved' => true], $resumed->output);
A child workflow started by a Call workflow node
is recorded too, so Workflow::assertRan('the-child') works without extra wiring.
Registering a fixture node
If your test needs a bespoke node (e.g. one that suspends on demand), register it
on the registry in setUp():
$this->app->make(\Qanna\WorkflowEngine\Engine\NodeRegistry::class)
->register(MyFakeApprovalNode::class);