Stateful property-based testing on top of Generators.
A stateful test exercises a system through a sequence of randomly chosen actions ("rules") applied to a state. Rules are constructed with Rule.create from a name and a step function that performs one application of the rule — drawing any arguments it needs from the test case and returning the new state. Invariants are 'state -> unit functions evaluated before any step is run and after every successful step.
To run a state machine, call run inside a Hegel test. Examples in this documentation assume open Hegel.
Example: an integer stack.
let push =
Stateful.Rule.create ~name:"push" ~step:(fun tc stack ->
let n =
draw tc (Generators.integers ~min_value:0 ~max_value:100 ())
in
n :: stack)
let pop =
Stateful.Rule.create ~name:"pop" ~step:(fun tc stack ->
assume tc (not (List.is_empty stack));
List.tl stack)
let%hegel_test test_integer_stack tc =
Stateful.run ~init:[] ~rules:[ push; pop ] tc
Sourceval run :
init:'state->rules:'stateRule.t list->?invariants:('state-> unit) list->test_case->
unit
Executes a stateful test by repeatedly applying randomly chosen rules to a state threaded from init, checking each of the invariants before the first step and after every successful step. Raises Invalid_argument if rules is empty.