Yes, stateful property tests can find workflow bugs such as skipped, repeated or reordered steps. You define the actions a user can take and the rules that must always hold. The tool generates many action sequences and checks the rules after each step, which suits checkout, refund and approval flows.
What stateful testing means
Ordinary property tests feed one input to one function. Stateful tests model a system that changes over time. In Hypothesis, rule based stateful tests work by defining primitive actions and letting the tool generate sequences of them, looking for any sequence that causes a failure.
The pieces, as the documentation describes them:
- Rules: functions marked with the
rule()decorator. A state machine needs at least one. - Initializes: a special kind of rule that runs exactly once before any normal rule.
- Invariants: checks marked with
invariant()that run after every step. - Preconditions: a function that says whether a rule may run in the current state. Rules whose preconditions fail are skipped.
Why this fits business flows
OWASP's testing guide recommends borrowing logical and finite state techniques from functional testers, and asks what happens when a user jumps from step one directly to step three. A stateful test asks that question thousands of times, in orders nobody would write by hand.
Think of an order:
- Rules: add item, apply coupon, pay, refund, cancel.
- Invariants: total paid never goes below zero, refunds never exceed payment, a coupon is applied once, stock never goes negative.
- Preconditions: you can refund only after paying.
Then watch what the tool finds. If a sequence of apply coupon, apply coupon, pay breaks the invariant, you have a logic flaw with a reproduction attached.
Steps to build one
- Write the invariants in plain English with the product owner.
- List the actions available through your API or service layer.
- Wrap each action in a rule that calls the real code against a test database.
- Add preconditions only where the product truly forbids an action. Leave other illegal moves reachable, since those are what attackers try.
- Run it locally, then in CI with a time limit.
- Pin each failing sequence as a regular test once fixed.
Keep it honest
| Risk | Mitigation |
|---|---|
| Model drifts from the real rules | Review invariants with product each quarter |
| Tests become slow | Use an in memory store or a transaction per run |
| Flaky failures | Seed time and randomness, see your determinism rules |
| False alarms from impossible states | Tighten preconditions, but only for truly impossible moves |
Preconditions deserve care. The documentation says Hypothesis skips rules whose preconditions fail, which is cleaner than calling assume inside many rules. But a precondition that blocks an action the real API allows hides the very bug you want, so mirror the server's actual checks, not the intended ones.
Stateful tests do not replace manual abuse case review. They cover sequences you can express as actions. Concurrency, such as two parallel redemption requests, needs separate tests that fire real simultaneous calls.
Try it yourself
Click through it at your own pace, or open it full screen. See how checks repeat over time instead of running once, which is the mindset behind invariants checked after every step.
Safeguard covers the dependency side of the same idea: continuous SBOM and dependency scanning, with a free scan to start. Workflow invariants remain your own tests to write.
Quick check
Is a stateful test the same as an integration test?
No. It can run against real code, but it generates the action sequences itself and checks invariants after every step.
Does it catch race conditions?
Not by itself. Sequential generated actions miss true parallelism, so add targeted concurrent tests.
Sources
Last reviewed: 19 October 2026.