OCaml By Examples

property tests

console

QCheck is a property-based testing framework: it allows you to generate arbitrary values of your custom types, and test properties of your choice on them (e.g. does my Car type always contain at least 4 wheels). For this example, we will test the [hexstring](https://github.com/mimoo/hexstring) library.
$ opam install qcheck hexstring

tests.ml

Let's create an `encode_decode` property test (1000 of them actually) that will generate random strings and enforce that they can be hex encoded and then decoded to the same string.
let encode_decode =
  QCheck.(
    Test.make ~count:1000 ~name:"encode -> decode" string (fun some_string ->
        let expected_result = Bytes.of_string some_string in
        let hexstring = Bytes.of_string some_string |> Hexstring.encode in
        Hexstring.decode hexstring = Ok expected_result))
Let's create another property test, that will generate valid hexadecimal strings and make sure that we can decode them (and re-encode them to the same hexstring).
To do that, we first need to tell QCheck how to generate an element of an hexstring : something that is either in the range a-f or the range 0-9.
let elem_gen = 
  let open QCheck.Gen in
  let c = char_range 'a' 'f' in
  let d = char_range '0' '9' in
  oneof [ c; d ]
Hexstrings are also even in side, never odd, so let's precise that using a natural number (`QCheck.Gen.nat`) and multiplying it by 2. By using `QCheck.Gen.map` we can transform generators.
let size_gen = 
  QCheck.Gen.(map (fun x -> 2 * x) nat)
We finally create the **value generator** that will be used by QCheck to create property tests.
let arbitrary_hexstring =
  QCheck.Gen.list_size size_gen elem_gen

let decode_arbitrary_hexstring =
We can use this generator of valid hexadecimal strings to ensure that decoding (and then re-encoding) always work.
  QCheck.(
    Test.make ~count:1000 ~name:"rand hexstring -> decode"
      (make arbitrary_hexstring) (fun arb ->
        let s = arb |> List.to_seq |> String.of_seq in
        let decoded = Hexstring.decode s in
        match decoded with
        | Ok r -> Hexstring.encode r = s
        | Error _  -> false
Finally, we can run these tests using QCheck's provided helper `run_tests_main` as main function of our executable.
let () =
  QCheck_base_runner.run_tests_main
    [ encode_decode; decode_arbitrary_hexstring ]

dune

(library
 (name lib)
 (libraries))

; run property tests with `dune runtest`
So that property tests can be included by the `dune runtest` commands, and in turn in test coverage (see next section), you can add an extra rule to your `dune` file.
(rule
 (alias runtest)
 (action
  (run ./tests.exe)))
next: test coverage