04 Testing with stub responses
Before you start
Complete 01 Linear pipelines. You should know how to register steps and read result.output. No pytest knowledge is required for the first example, though the Verify section mentions optional pytest markers.
Concept
workflow.test(cases) runs your workflow in test mode. The Rust runtime returns deterministic step outputs from a stub_responses map instead of calling a live provider. This is how you assert pipeline shape in CI without API keys or network access.
Each case is a dict:
| Field | Purpose |
|---|---|
name | Label for the result row (optional) |
input | Run input string |
expected_output | If set, pass/fail compares final output to this string |
stub_responses | Map of step keys to mock behavior |
Stub keys follow step order: step_1, step_2, step_3, and so on (not agent names).
Each stub entry can include:
| Stub field | Behavior |
|---|---|
output | Text returned when the step succeeds |
fail_times | How many attempts fail before success |
then_output | Output after failures are exhausted |
When you set expected_output without stub_responses, the SDK defaults to {"step_1": {"output": expected_output}} for single-step workflows.
test() returns a list of result dicts with at least name, passed, and output. Recovery is disabled in test mode, so Postgres is not required.
Example
Save as stub_test_demo.py:
Run:
You pinned both step outputs. The final string must match expected_output for passed to be True.
Verify
| Check | Expected |
|---|---|
| Happy path case | passed is True |
| Wrong expected output | passed is False when final text differs |
| Failure then recovery stub | fail_times plus then_output succeeds without live retry config |
Failure recovery stub (single step):
Expected: True recovered.
Optional pytest marker (requires arcflow.testing.pytest_plugin):
See sdk-python/tests/test_workflow_test.py for full fixture examples.
Next
05 Retry and timeouts basics configures runtime retry and timeout limits that pair with test mode for failure drills.