# Journal: Property-Based Testing with PropEr, Erlang, and Elixir

To be a more productive reader when rereading a book, it is very convenient to create small rebar3 projects based on books’ samples and ideas. Here’s what I’ve already made.

5 Likes

2 Likes

## Exercise 2

I add output and then run it to see it execute.
There is peace of my code with output:

``````increments([Head | Tail]) -> increments(Head, Tail).

increments(_, []) ->
io:format("~n"),
true;
if length(Tail) > 0 -> io:format(", ");
true -> io:format("|")
end,
increments(_, _) -> false.
``````
3 Likes

`Erlang`
(page 88)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

I would like to share inner step calculation while gathering values (by groups of 10). I was confused a litter how is the calculation going.

I have inserted an expression to print the parameters.

``````prop_collect2() ->
?FORALL(Bin, (binary()),
(collect(to_range(10, byte_size(Bin)),
is_binary(Bin)))).

%%%%%%%%%%%%%%%
%%% Helpers %%%
%%%%%%%%%%%%%%%
to_range(M, N) ->
Base = N div M,
io:format("~2B = ~2B div ~B | {~2B, ~B} = {~2B * ~B, (~B + 1)*~B~n",
[Base, N, M, Base * M, (Base + 1) * M, Base, M, Base, M]),
{Base * M, (Base + 1) * M}.
``````

Command:
`rebar3 proper​ -p prop_collect2`

Output (with the intermediate calculations):

`````` 0 =   0 div 10 | { 0, 10} = { 0 * 10, (0 + 1)*10
. 0 =  1 div 10 | { 0, 10} = { 0 * 10, (0 + 1)*10
. 0 =  0 div 10 | { 0, 10} = { 0 * 10, (0 + 1)*10
. 0 =  0 div 10 | { 0, 10} = { 0 * 10, (0 + 1)*10
...
. 3 = 38 div 10 | {30, 40} = { 3 * 10, (3 + 1)*10
. 3 = 33 div 10 | {30, 40} = { 3 * 10, (3 + 1)*10
. 1 = 15 div 10 | {10, 20} = { 1 * 10, (1 + 1)*10
. 2 = 22 div 10 | {20, 30} = { 2 * 10, (2 + 1)*10

OK: Passed 100 test(s).

56.00% {0,10}
28.00% {10,20}
9.00% {20,30}
7.00% {30,40}
===>
1/1 properties passed
``````
2 Likes

`Erlang`
(page 89)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

While running property based test `prop_dupes` you will get the same logging info.

``````prop_dupes() ->
?FORALL(KV, list({key(), val()}),
begin
M = maps:from_list(KV),
_ = [maps:get(K, M) || {K, _V} <- KV], % crash if K's not in map
collect(
{dupes, to_range(5, length(KV) - length(lists:ukeysort(1,KV)))},
true
)
end).

%%%%%%%%%%%%%%%
%%% Helpers %%%
%%%%%%%%%%%%%%%
key() -> integer().
val() -> term().

to_range(M, N) ->
Base = N div M,
io:format("~2B = ~2B div ~B | {~2B, ~2B} = {~2B * ~B, (~B + 1)*~B~n",
[Base, N, M, Base * M, (Base + 1) * M, Base, M, Base, M]),
{Base * M, (Base + 1) * M}.
``````

## Full code project

2 Likes

`Erlang`
(page 91)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

I have compared results alternative PropEr generators - oneof, union and elements. Here’s what I noticed. The first two generators after 10 launches in the resulting report give 3-4 lines, and the third only 3.

Source code projects:

• ex05_oneof
82.00% {dupes,{0,5}}
11.00% {dupes,{5,10}}
5.00% {dupes,{10,15}}
2.00% {dupes,{15,20}}
===>
1/1 properties passed
• ex06_union
83.00% {dupes,{0,5}}
10.00% {dupes,{5,10}}
5.00% {dupes,{10,15}}
2.00% {dupes,{15,20}}
===>
1/1 properties passed
• ex07_elements
82.00% {dupes,{0,5}}
13.00% {dupes,{5,10}}
5.00% {dupes,{10,15}}
===>
1/1 properties passed
2 Likes

`Erlang`
(page 92)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

``````prop_aggregate() ->
Suits = [club, diamond, heart, spade],
?FORALL(Hand, (vector(5, {oneof(Suits), choose(1, 13)})),
begin
% io:format("~p~n", [Hand]),
aggregate(Hand, true)
end). % `true' makes it always pass

``````

If you would like to know what info contains `Hand` variable, just uncomment logging line.

Full code project

Note that what kind of PropEr API here is. There are generators and wrapper that can be used to collect statistics on the distribution of test data:

2 Likes

`Erlang`
(page 97)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

To read more about `resize(Size, Generator)` function, please go to the documentation page.

Full code project

2 Likes

`Erlang`
(page 98)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Note that in the property `prop_profile1`proplists module is used.

2 Likes

`Erlang`
(page 99)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Note that macros `?SIZED(VarName, Expression)` is contained in the `include/proper_common.hrl` file.
It is `proper_types:sized(fun(VarName) -> Gen end))` function call.

2 Likes

`Erlang`
(page 100)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Note that when you lounch propterties in a command line there should be no spaces between names in the property name list `-p prop_profile1,prop_profile2`:
`\$ rebar3 proper -m prop_generators -p prop_profile1,prop_profile2`

Otherwise, you will receive an error message with the following content:

``````===> Property [] does not belong to any module in ["prop_generators"]
``````
2 Likes

`Erlang`
(page 100)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Let’s take a closer look at the expression `?SIZED(Size,resize(min(100,Size)*35,string()))`.

Note that there is `erlang:min` function is used. To ensure maximum size without imposing the maximum value you can use in the `erlang:max` function:

``````OK: Passed 100 test(s).

27.00% {name,{0,10}}
12.50% {name,{10,20}}
8.00% {name,{20,30}}
5.50% {bio,{2400,2700}}
5.50% {bio,{3000,3300}}
5.00% {bio,{0,300}}
5.00% {bio,{300,600}}
5.00% {bio,{1800,2100}}
4.50% {bio,{1500,1800}}
3.50% {bio,{600,900}}
3.50% {bio,{900,1200}}
3.50% {bio,{1200,1500}}
3.50% {bio,{2700,3000}}
3.00% {bio,{2100,2400}}
2.50% {bio,{3300,3600}}
2.50% {name,{30,40}}
===>
1/1 properties passed
``````

Full code project

2 Likes

`Erlang`
(page 102)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Note that binding is the main purpose of this macro `?LET`. It is implemented using a function `proper_types:bind(RawType,fun(X) -> Gen end,false)`.
Definition `?LET` macro contains in `include/proper_common.hrl` file.

More information related to `queue` module you can see in the Erlang documentation.

Macros `?LET` helps create new generator `queue`.

2 Likes

`Erlang`
(page 103)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`

Pay attention to the peculiarity in the implementation of the macro `?SUCHTHAT`. It is a function call

``````-define(SUCHTHAT(X,RawType,Condition),
``````

Note that in your code (`test/prop_generators.erl`) you can not define `non_empty` generator because it is already defined in ProEr API. If you try to do it we will get an error message:

``````===> Compiling test/prop_generators.erl failed
test/prop_generators.erl:19: defining imported function non_empty/1
``````

Let’s see how `non_empty/1` generator can be used - rebar3 project.

2 Likes

`Erlang`
(page 104)
`code/CustomGenerators/erlang/pbt/test/prop_generators.erl`
Explore Filtering
It was interesting for me to check (reproduce) the error described by the author - the unsuccessful creation of the generator when filtering using `?SUCHTHAT` macro.

• Making mistakes is fun.
• Messages contain more details to help you with testing. Here is the full text of one of the possible errors:
`\$ rebar3 proper -p prop_list_content_integer`
``````Error: Couldn't produce an instance that satisfies all strict constraints from (prop_generators:check_list_content_integer/1) after 50 tries.
===>
0/1 properties passed, 1 failed
===> Failed test cases:
prop_generators:prop_list_content_integer() -> {error,
{cant_generate,
[{prop_generators,
check_list_content_integer,
1}]}}
``````

2 Likes

`Erlang`
(page 104)
`test/prop_generators.erl`

In order to better understand the difference between macros `?LET` and `?SUCHTHAT`, try to implement of your own examples and you will see this difference.
Practice at work is very important, and doing exercises such as this is a wonderful solution.
Here is a project that demonstrates the use of these macros.

2 Likes

`Erlang`
(page 105)
`test/prop_generators.erl`

Latin1

When you start running property filtering Latint1 characters

``````rebar3 proper -p prop_latin1_string
``````

you, likely, received a message from PropEr:

``````Error: Couldn't produce an instance that satisfies all strict constraints from (prop_generators:latin1_string2/0) after 50 tries.
``````

To increase the number of attempts to 80 (this number of attempts should be enough), use the PropEr option `--constraint_tries`.

The complete command will look like this:

``````rebar3 proper -p prop_latin1_string --constraint_tries 80
``````

To simplify the problem being solved - generating encoded Latin1 strings, it would be better to set a numerical segment and generate numbers (codes by symbols) only from printable segment:

``````prop_latin1_string2() ->
?FORALL(String, latin1_string2(),
is_list(String)
).

latin1_string2() ->
?SUCHTHAT(S, ?SIZED(Size,vector(Size,integer(33, 126))), io_lib:printable_latin1_list(S)).
``````

Now this property does not need any additional parameters of the testing framework PropEr.

Project

2 Likes

`Erlang`
(page 105)
`test/prop_generators.erl`

Unicode strings

For generating Unicode strings, the function proper_unicode:utf8() is quite handy.

To suppress the generation of empty strings, use the non_empty/1 generator.

You will find the implementation of the ideas for generating Unicode strings, which the author described, in the project.

2 Likes

`Erlang`
(page 106)
`test/prop_generators.erl`

For implementing predictable tests, the generator frequency is well suited for probability management.

Examples of using this this generator in the property based tests can be found in the rebar3 project.

2 Likes

`Erlang`
(page 108)
`test/prop_generators.erl`

It is best to view the result of the methods’ work directly in the property code. I made it.
To make the test more convenient for creating properties, I added non_empty generator.

rebar3 project

2 Likes