Skip to main content

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

AssertionPasses 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);