Dividing with ?LETSHRINK

Learn the ?LETSHRINK macro and where it can come in handy to understand the test cases that fail.

We'll cover the following

The ?LETSHRINK macro

As we use PropEr, we sometimes get stuck with generators creating huge data structures that take a long time to shrink and often don’t give very interesting results back. This often happens when some very low-probability failure is triggered, meaning that the framework had to generate a lot of data to find it, and has limited chances of shrinking things in a significant manner.

Whenever that happens, the ?LETSHRINK([Pattern, ...], [Generator, ...], Expression) is what we need. Let’s take a look at how we can use the generators in practice.

?LETSHRINK([A,B,C], [list(number()), list(number()), list(number())], 
        A ++ B ++ C)

The macro looks a lot like a regular ?LET macro, but with a few constraints. The first two arguments must always be lists, and the third argument is an operation where all list elements get combined into one. Here, A, B, and C are three lists filled with integers, and A ++ B ++ C is just a bigger list of integers. The important part is that the program can use any of A, B, or C instead of A ++ B ++ C.

The reason for that is that once a property fails and PropEr tries to shrink the data set, it will instead pick just one of A, B, or C without applying the transformation and return that directly. The ?LETSHRINK macro is particularly appropriate for recursive structures, data made through branching, and all kinds of pieces of data that are generated by smashing others together and applying transformations, since taking a part of it is a legitimate way to get a simpler version.

Basically, we’re giving PropEr a way to divide the data up to isolate a failing subset more efficiently.

Example

Let’s take a look at an example of how the macro can be used in properties.

The most common form of ?LETSHRINK is the one we’d use on tree data structures. Let’s take a look at the binary tree generator of size N.

  • To run the tree_shrink generator, use proper_gen:sampleshrink(prop_shrink:tree_shrink(16)).

  • To run the tree generator, use proper_gen:sampleshrink(prop_shrink:tree(4))..

Get hands-on with 1200+ tech skills courses.