package ppx_test

  1. Overview
  2. Docs
A ppx replacement of pa_ounit


Dune Dependency






ppx_test tries to replace pa_ounit. It provides the following syntax sugars:

  • with_location e, returns e and its source code location
  • module_path, returns the current module path name
  • let %TEST name = e, a replacement of TEST name = e
  • let %TEST_UNIT name = e, a replacement of TEST_UNIT name = e

Published: 12 Jan 2018


ppx_test : ppx replacement of pa_ounit


ppx_test is a PPX preprocessor to embed tests in source code and to run these tests and collect the results.

Embedding tests are by special let %TEST declarations. For example:

let %TEST split_at = split_at 3 "hello world" = ("hel", "lo world")

A let %TEST declaration is converted to code which registers the test function. The registered tests are executed by calling Ppx_test.Test.collect ().


ppx_test is build as a PPX port of CamlP4 module pa_test.

Pros and Cons


  • Test declarations are valid in OCaml's syntax. Therefore it can benefit the editor support such as source code highlighting and auto-indentaitons.

  • No special test code extraction is required to type-check, compile and run them.


  • Tests are embeded. Therefore it increases object size of the libraries and applications.


ppx_test converts the embeded test codes using the following functions:

  • PTest.test : Ppx_test.Location.t -> string option -> (unit -> unit) -> unit

  • PTest.test_unit : Ppx_test.Location.t -> string option -> (unit -> unit) -> unit

  • PTest.test_fail : Ppx_test.Location.t -> string option -> (unit -> unit) -> unit

It is the client responsibility to make the module PTest available in the name space where the tests are embeded.

Ppx_test.Test is a ready-to-use example for PTest. See the later section how to use it. Another example, examples/ provides a simple wrapper for pa_ounit.

How to declare tests

Inlined test let %TEST

Test expression e can be embedded using let %TEST toplevel declaration as follows:

let %TEST <name> = e

Test names <name> can be one of the following:

  • _ : anonymous

  • "name" : string

  • name : variable

  • M.X : "constr_longident"

Names except _ are identified with the current module path. For example, in the following code,

(* *)
module M = struct
  let %TEST test = ...

The test has the global name X.M.test. If the file is compiled with -for-package P, then it is prefixed as P.X.M.test.

Boolean test

By default, tests are all boolean. The test code e in let %TEST name = e must have type bool. The test succeeds when e is evaluated to true.

For example,

let %TEST add = 1 + 2 = 3

Unit test

If a name is not anonymous and ends with _ ex. let %TEST name_ = e, it is considered unit test: the test expression e must have type unit. The test succeeds when e is evaluated without raising any exception.

For example,

let %TEST add_ = assert (1 + 2 = 3)

This naming convention of _ follows Haskell function naming (ex. mapM_ :: Monad m => (a -> m b) -> [a] -> m ().)

Failure test

If a name is not anonymous and end with _fail ex. let %TEST name_fail = e, it is considered failure test: the test expression e must have type unit. The test succeeds when e's evaluation raises any exception.

For example,

let div100 x = 100 / x
let %TEST div100_10 = div100 10 = 10  (* boolean test *)
let %TEST div100_fail = div100 0      (* fail test *)

Group of tests by [%%TEST ..]

You can write a bunch of tests inside [%%TEST ..]:


  let add = 1 + 2 = 3              (* boolean test *) 
  let add_ = assert (1 + 2 = 3)    (* unit test *)

You can also omit the name of the test in [%%TEST]:

   length [1;2;3] = 3;;

This is equivalent with let %TEST _ = length [1;2;3] = 3.

Testing order

Tests are listed in their order of registeration: in the same order of their occurrences and module linking.
If tests are inside a functor, they are added when the functor is applied: if not applied, tests are ignored.

Tests running order can be shown using --test-show option, but you should not rely on it: tests should be independent each other.

Tests inside functors

Tests inside functors are only registered when the functors are applied and the code registeration by let %TEST .. or [%%TEST ..] are executed.


__FOR_PACKAGE__ is a pseudo value of type string option which returns the package name specified by comipler's -for-package option.

Dependencies (5)

  1. ppxx >= "2.3.0" & < "2.4.0"
  2. re
  3. jbuilder >= "1.0+beta7"
  4. ocamlfind build & >= "1.5.6"
  5. ocaml >= "4.03.0"

Dev Dependencies


Used by (2)

  1. levenshtein >= "1.1.3"
  2. spotlib = "4.0.3"




Innovation. Community. Security.