PropEr is a QuickCheck-inspired property-based testing tool for Erlang. In contrast to traditional testing methodologies, the tester only has to provide the generic structure of valid inputs and some properties, which specify the relation between input and output. The testing tool will then produce progressively more complex valid inputs and compare the results it gets to what it expected (based on the defined properties).
PropEr is one such testing tool for Erlang. It is open-source and supports Erlang R15B01+.

Assuming you already have your $ERL_LIBS set up, you can simply run the following commands to install PropEr and make it available globally.

1
2
3
4
cd $ERL_LIBS
git clone git://github.com/manopapad/proper.git
cd proper
make fast

After that, you can use PropEr in all your Erlang projects.

I like using EUnit as the runner for my property based tests. There are some caveats, but all in all it works without any issues.
What I don’t like is defining my property-based tests in the same module as my unit tests, because it feels wrong to do so. I generally define one _test_ function that calls proper:module/2.

1
2
proper_module_test_() ->
  {timeout, 180, ?_assertEqual([], proper:module(example_utils_prop, [long_result, {to_file, user}]))}.

This allows me to have all of the unit tests in example_utils_test and all of the property-based tests in example_utils_prop.

Let’s write a simple property-based test for the binary_join function I’ve [introduced in February]({{ site.url }}/posts/joining-a-list-of-binaries-in-erlang/).

1
2
3
4
5
6
7
8
9
prop_binary_join() ->
  ?FORALL({Bs, S}, {list(binary()), binary()}, begin
    ExpectedLength = if
      length(Bs) =:= 0 -> 0;
      length(Bs) =:= 1 -> byte_size(hd(Bs));
      true -> lists:foldr(fun(B, Acc) -> byte_size(B) + Acc end, 0, Bs) + ((length(Bs) - 1) * byte_size(S))
    end,
    ExpectedLength =:= byte_size(example_utils:binary_join(Bs, S))
  end).

This will test if the size of the output of example_utils:binary_join/2 is what we would expect:

  • Zero: If the list of binaries to join is empty
  • Size of the first element in the list: If the list of binaries to join only has one element
  • Sum of the size of all elements in the list, plus the size of the separator times the number of elements (minus one, because there will be no trailing separator): All other cases

The ?FORALL macro takes three aguments:

  1. Tuple of variables which will be set to the genrerated inputs and can be accessed in the property
  2. Tuple of type specifications for the inputs that will be generated
  3. The property that will be tested

If you actually run the test, the output will look something like this:

Testing example_utils_prop:prop_binary_join/0
....................................................................................................
OK: Passed 100 test(s).

By default, PropEr will generate 100 random valid inputs and compare the output to the (length) properties we defined above.

PropEr can do much more than what I’ve shown here. While there are not many examples available, you should be able to get going using the documentation.