- Published on
Unit testing OCaml code with ppx_expect: some notes
- Chris Armstrong
ppx_expect allows you to write inline tests which sit with your code and can be run easily with dune.
It works is a rather novel way: you print out a string in your test and then assert against the printed result.
This has the advantage of allowing you to build your tests retrospectively (inverting TDD): you can write the code that generates the test and then update it to the correct value. This can be much faster that trying to guess the output and refactoring it to work.
Things to know using ppx_expect
- It only works on libraries, not executables. If you're building your code with dune, move the code to be tested into a
(library (name my_library)...)declaration, and then reference that library in your executable as
(executable ... (libraries my_library))
- ppx_expect has a lot of JaneStreet dependencies, like
Core(something to be mindful of if you don't want them as transitive dependencies).
fmtis useful to assist with printing format strings and
showplugin is indispensible for printing your types.
ppx_expect has to be added as a project dependency first. If you're using
dune-project to manage your opam files, add it in the
depends clause like so:
(package) ; ... (depends fmt ppx_deriving ppx_expect) )
You also need to install it into your opam switch:
opam install ppx_expect
In your dune library (ppx_expect does not work with executables), add
(inline_tests) and the
(preprocess ppx_expect) reference:
(library (name my_library) (libraries fmt) (inline_tests) (preprocess (pps ppx_expect ppx_deriving.show)) )
Here is an example with a simple odd/even generator:
type odd_even = Odd | Even [@@deriving show] let is_odd_even x = if x mod 2 = 0 then Even else Odd let%expect_test "is_odd_even 0" = let res = (is_odd_even 0) in Fmt.pr "%a" pp_odd_even res; [%expect "My_library.Even"] let%expect_test "is_odd_even 5" = let res = (is_odd_even 5) in Fmt.pr "%a" pp_odd_even res; [%expect "My_library.Odd"] let%expect_test "is_odd_even -10" = let res = (is_odd_even (-10)) in Fmt.pr "%a" pp_odd_even res; [%expect "My_library.Even"]
Executing test suite
We can execute our unit tests with:
If your tests are successful, you will see no output.
Let's add an error test to see what that looks like:
let%expect_test "is_odd_even 13" = let res = (is_odd_even (13)) in Fmt.pr "%a" pp_odd_even res; [%expect "My_library.Even"]
We will now see error output:
File "my_library.ml", line 1, characters 0-0: diff --git a/_build/default/my_library.ml b/_build/.sandbox/89010188fcdb897a907873b6c7c65c59/default/my_library.ml.corrected index f9683ce..4093b19 100644 --- a/_build/default/my_library.ml +++ b/_build/.sandbox/89010188fcdb897a907873b6c7c65c59/default/my_library.ml.corrected @@ -21,4 +21,4 @@ let%expect_test "is_odd_even 5" = let%expect_test "is_odd_even 13" = let res = (is_odd_even (13)) in Fmt.pr "%a" pp_odd_even res; - [%expect "My_library.Even"] + [%expect "My_library.Odd"]