Triq QuickCheck for Erlang
By and large, the Triq API is modeled closely after QuviQ eqc
, except you
want to replace any occurrence of eqc
with triq
. The main supporting module
is called triq_dom
, corresponding to eqc’s eqc_gen
.
This is a fork of Triq that is being run under the ZeroMQ Collaberation rules, https://rfc.zeromq.org/spec:22 with the one exception being that it is under the Apache licence.
To use triq
, you can add it as a project dependency and let your
package manager of choice handle it:
rebar.config: {triq, "1.*"}
erlang.mk: DEPS = triq
mix.exs: {:triq, "~> 1.*"}
If you want to make triq
available globally, you can install it from
source into your Erlang installation by adding it in one of your
$ERL_LIBS
paths. So, it’s either somewhere like
/usr/lib/erlang/lib
or $HOME/.erl
.
You can either download a tagged release from
https://gitlab.com/triq/triq/tags
and extract that or clone the
git repo https://gitlab.com/triq/triq
in the target directory. Once
that’s done, cd into the directory and run make
.
Now, if you start erl
, you should be able to call functions from the
triq
module.
$ erl 1> code:which(triq). "/usr/lib/erlang/lib/triq/ebin/triq.beam" 2>
To write properties with triq
, include the header file:
-include_lib("triq/include/triq.hrl").
And you’re ready to write property tests. An example property could be:
prop_append() -> ?FORALL({Xs,Ys},{list(int()),list(int())}, lists:reverse(Xs++Ys) == lists:reverse(Ys) ++ lists:reverse(Xs)).
To test this property, run triq:check/1
, thus:
1> triq:check(prop_append()). ...................................................................... .............................. Ran 100 tests true 2>
If the test fails, it will try to shrink the result; here is an example:
prop_delete() -> ?FORALL(L,list(int()), ?IMPLIES(L /= [], ?FORALL(I,elements(L), ?WHENFAIL(io:format("L=~p, I=~p~n", [L,I]), not lists:member(I,lists:delete(I,L)))))).
Which runs like this:
1> triq:check(triq_tests:prop_delete()). x....Failed! L=[4,5,5], I=5 Failed after 5 tests with false Simplified: L = [0,0] I = 0 false 2>
You can get the values used for the failing test with counterexample
,
and reuse the same test values with check/2
:
3> A = triq:counterexample(triq_tests:xprop_delete()). x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxFailed! L=[3,2,1,1,1], I=1 Failed after 101 tests with false Simplified: L = [0,0] I = 0 [[0,0],0] 4> A. [[0,0],0] 5> triq:check(triq_tests:xprop_delete(), A). Failed! L=[0,0], I=0 Failed after 1 tests with false Simplified: L = [0,0] I = 0 false 6>
Modules compiled with the triq.hrl
header, auto-export all functions
named prop_*
, and have a function added called check/0
which runs
triq:check/1
on all the properties in the module.
1> mymodule:check().
You can also instruct triq
to generate EUnit tests for each property
which allow the module to be treated like an ordinary EUnit test
suite. This is highly recommended and avoids the need for triq
or
generic qc
support in your build/test tool of choice. To achieve
that, just make sure to include the attribute -triq(eunit).
at the
top of the module. Thus, the initial triq.hrl
include would turn
into this:
-include_lib("triq/include/triq.hrl"). -triq(eunit).