Page
Library
Module
Module type
Parameter
Class
Class type
Source
WindtrapSourceWindtrap - One library for all your OCaml tests.
Unit tests, property-based tests, snapshot tests, and expect tests in a single library. Tests are organized as a tree of cases and groups, run with run.
open Windtrap
let () =
run "MyTests"
[
group "Calculator"
[
test "addition" (fun () -> equal int 5 (add 2 3));
test "subtraction" (fun () -> equal int 1 (sub 3 2));
];
test "standalone" (fun () -> is_true true);
]Internal. Runtime support for ppx_windtrap. Used by PPX-generated code; not intended for direct use.
Type-safe equality witnesses used by assertions (equal, not_equal) and property tests (prop). Compose them freely: pair int string, list (option int), etc.
These are re-exported from Testable for convenience. Use the Testable module directly for construction helpers like Testable.make and accessors like Testable.pp.
Alias for Testable.t.
Like int but generates integers in [-10_000, 10_000]. Useful for properties involving arithmetic that would overflow with full-range integers.
Like int but generates non-negative integers in [0, 10_000], biased towards small values. Useful for indices, sizes, and counts.
float_rel ~rel ~abs creates a testable with both relative and absolute tolerance.
Testable for Either.t values.
Testable for lists. Produces highlighted diffs on failure.
Testable for arrays. Produces highlighted diffs on failure.
Always considers values equal. Useful for ignoring parts of a structure.
slist t cmp compares lists as sets, ignoring order.
of_equal eq creates a testable from an equality function.
contramap f t transforms values with f before comparing.
val testable :
pp:'a Pp.t ->
?equal:('a -> 'a -> bool) ->
?gen:'a Windtrap_prop.Gen.t ->
?check:('a -> 'a -> 'a Testable.check_result) ->
unit ->
'a testabletestable ~pp () creates a custom testable. Shorthand for Testable.make.
let point = testable ~pp:pp_point ~equal:equal_point ()
let tensor = testable ~pp:pp_tensor ~gen:gen_tensor ()An abstract test or group of tests.
Source position as produced by __POS__.
Source position as produced by [%here].
val test :
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
?retries:int ->
string ->
(unit -> 'a) ->
testtest name fn creates a test case. The return value of fn is ignored.
val group :
?pos:pos ->
?tags:Tag.t ->
?setup:(unit -> unit) ->
?teardown:(unit -> unit) ->
?before_each:(unit -> unit) ->
?after_each:(unit -> unit) ->
string ->
test list ->
testgroup name children creates a test group. Tags are inherited by children.
val ftest :
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
?retries:int ->
string ->
(unit -> 'a) ->
testftest name fn creates a focused test case. When any focused test or group exists, only focused tests run. A warning is printed to remind you to remove focus markers before committing.
val fgroup :
?pos:pos ->
?tags:Tag.t ->
?setup:(unit -> unit) ->
?teardown:(unit -> unit) ->
?before_each:(unit -> unit) ->
?after_each:(unit -> unit) ->
string ->
test list ->
testfgroup name children creates a focused test group. All tests inside a focused group are treated as focused.
val slow :
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
?retries:int ->
string ->
(unit -> 'a) ->
testslow name fn creates a test tagged with Tag.speed.Slow, skipped when ~quick:true is passed to run. The return value of fn is ignored.
val bracket :
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
?retries:int ->
setup:(unit -> 'a) ->
teardown:('a -> unit) ->
string ->
('a -> 'b) ->
testbracket ~setup ~teardown name fn creates a test with setup and teardown. setup runs before the test to acquire a resource, which is passed to fn. teardown runs after the test (even on failure) to release the resource.
Partial application creates reusable test constructors:
let with_db = bracket ~setup:connect_db ~teardown:close_db
group "Database" [
with_db "query works" (fun db -> ...);
with_db "insert works" (fun db -> ...);
]cases testable cases name fn creates a group with one test per case.
Test names are generated as "name (case_value)" using the testable's pretty-printer.
cases (triple int int int)
[ (2, 3, 5); (0, 0, 0); (-1, 1, 0) ]
"addition"
(fun (a, b, expected) -> equal int expected (a + b))fixture create returns a lazy accessor. The resource is created on first call and cached for subsequent calls.
let get_db = fixture (fun () -> Db.connect ())
group "Database"
[ test "query" (fun () -> let db = get_db () in ...);
test "insert" (fun () -> let db = get_db () in ...);
]val run :
?quick:bool ->
?bail:int ->
?fail_fast:bool ->
?output_dir:string ->
?stream:bool ->
?update:bool ->
?snapshot_dir:string ->
?filter:string ->
?exclude:string ->
?failed:bool ->
?list_only:bool ->
?format:format ->
?junit:string ->
?seed:int ->
?timeout:float ->
?prop_count:int ->
?tags:string list ->
?exclude_tags:string list ->
?argv:string array ->
string ->
test list ->
unitrun name tests runs the test suite and exits the process.
Tests with the "disabled" label are automatically skipped.
Configuration is resolved with the following priority (highest first): programmatic arguments, CLI flags, environment variables, defaults.
equal testable expected actual asserts that expected equals actual.
not_equal testable a b asserts that a does not equal b.
is_true b asserts that b is true.
is_false b asserts that b is false.
is_some opt asserts that opt is Some _.
is_none opt asserts that opt is None.
some testable expected opt asserts that opt is Some expected.
is_ok r asserts that r is Ok _.
is_error r asserts that r is Error _.
val ok :
?here:here ->
?pos:pos ->
?msg:string ->
'a testable ->
'a ->
('a, 'b) result ->
unitok testable expected r asserts that r is Ok expected.
val error :
?here:here ->
?pos:pos ->
?msg:string ->
'b testable ->
'b ->
('a, 'b) result ->
uniterror testable expected r asserts that r is Error expected.
raises exn fn asserts that fn () raises exn.
Uses structural equality (the = operator) to compare exceptions. This works for most exceptions but may not behave as expected for exceptions carrying mutable data, closures, or abstract types. Use raises_match for such cases.
val raises_match :
?here:here ->
?pos:pos ->
?msg:string ->
(exn -> bool) ->
(unit -> 'a) ->
unitraises_match pred fn asserts that fn () raises an exception matching pred.
no_raise fn asserts that fn () does not raise, and returns its result.
val raises_invalid_arg :
?here:here ->
?pos:pos ->
?msg:string ->
string ->
(unit -> 'a) ->
unitraises_invalid_arg expected_msg fn asserts that fn () raises Invalid_argument with message expected_msg. On failure, shows the expected vs actual exception message.
test "negative size" (fun () ->
raises_invalid_arg "size must be positive" (fun () ->
create ~size:(-1) ()))raises_failure expected_msg fn asserts that fn () raises Failure with message expected_msg. On failure, shows the expected vs actual exception message.
failf fmt ... fails the test with a formatted message.
skip ?reason () skips the current test. Use when a test cannot run due to environment constraints.
test "windows only" (fun () ->
if not Sys.win32 then skip ~reason:"not on Windows" ()
(* ... windows-specific test ... *))snapshot ~pos:__POS__ actual compares actual against a stored snapshot. Create snapshots with WINDTRAP_UPDATE=1 or ~update:true.
snapshot_pp ~pos:__POS__ pp value pretty-prints value and snapshots the result.
val snapshotf :
?here:here ->
?pos:pos ->
?name:string ->
('a, Format.formatter, unit, unit) format4 ->
'asnapshotf ~pos:__POS__ "format" ... formats and snapshots the result.
output () returns and consumes stdout since last expect/output call. Use for post-processing non-deterministic output before comparison.
test "timing" (fun () ->
slow_operation ();
let out = output () in
let masked = mask_timing out in
expect masked "Done in XXms")expect expected compares stdout (normalized) against expected.
Normalization strips trailing whitespace per line and removes leading/trailing blank lines. This matches ppx_expect's default behavior.
test "greet" (fun () ->
print_endline " Hello ";
expect "Hello")Multiple expects consume output incrementally:
test "multi" (fun () ->
print_endline "First";
expect "First";
print_endline "Second";
expect "Second")expect_exact expected compares stdout exactly, with no normalization. Use when whitespace is semantically significant.
capture fn expected runs fn, captures stdout, compares (normalized).
test "compute" (fun () ->
let x =
capture
(fun () ->
print_string "ok";
42)
"ok"
in
equal int 42 x)capture_exact fn expected is like capture but with no normalization.
Property tests generate random inputs and check that properties hold for all generated values. On failure, counterexamples are automatically shrunk to find minimal failing cases.
Testables with generators (like int, list, etc.) can be used directly for property testing:
open Windtrap
let () =
run "Properties"
[
prop "reverse is involutive" (list int) (fun l ->
List.rev (List.rev l) = l);
prop "append length"
(pair (list int) (list int))
(fun (l1, l2) ->
List.length (l1 @ l2) = List.length l1 + List.length l2);
]Random value generators with integrated shrinking. For advanced use cases where you need to create custom generators.
val prop :
?config:Windtrap_prop.Prop.config ->
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
string ->
'a testable ->
('a -> bool) ->
testprop name testable law creates a property test.
Generates random values using testable's generator and checks that law holds for all. If a counterexample is found, it is automatically shrunk to find a minimal failing case.
prop "reverse is involutive" (list int) (fun l ->
List.rev (List.rev l) = l)val prop' :
?config:Windtrap_prop.Prop.config ->
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
string ->
'a testable ->
('a -> unit) ->
testprop' name testable fn is like prop but uses Windtrap assertions.
The property passes if fn completes without raising. Use Windtrap assertions like equal inside fn.
prop' "sorted list is sorted" (list int) (fun l ->
let sorted = List.sort Int.compare l in
(* Use Windtrap assertions *)
is_true (is_sorted sorted))val prop2 :
?config:Windtrap_prop.Prop.config ->
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
string ->
'a testable ->
'b testable ->
('a -> 'b -> bool) ->
testprop2 name t1 t2 law is a convenience for two-argument properties.
val prop3 :
?config:Windtrap_prop.Prop.config ->
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
string ->
'a testable ->
'b testable ->
'c testable ->
('a -> 'b -> 'c -> bool) ->
testprop3 name t1 t2 t3 law is a convenience for three-argument properties.
val prop4 :
?config:Windtrap_prop.Prop.config ->
?pos:pos ->
?tags:Tag.t ->
?timeout:float ->
string ->
'a testable ->
'b testable ->
'c testable ->
'd testable ->
('a -> 'b -> 'c -> 'd -> bool) ->
testprop4 name t1 t2 t3 t4 law is a convenience for four-argument properties.
assume b discards the current test case if b is false.
Use to filter out invalid inputs. Prefer constrained generators when possible, as excessive discarding can cause tests to give up.
prop "division" (pair int int) (fun (a, b) ->
assume (b <> 0);
(a / b * b) + (a mod b) = a)reject () unconditionally discards the current test case.
collect label records label for the current property case.
Labels are reported as a distribution over successful (non-discarded) property cases. Calling collect multiple times with the same label in one case counts once.
classify label cond is if cond then collect label.
cover ~label ~at_least cond declares a required minimum coverage for label, and records a hit when cond is true.
Coverage is measured over successful (non-discarded) cases and checked once the property has generated all required cases. If unmet, the property fails with a coverage report.