It is a challenge to write good and effective properties and often requires more effort than standard tests. Although it can take a long time, we’re going to try to speed things up.
There are a few tips and tricks we can use to help us figure out how to write decent properties in tricky situations. These are:
To start, we’ll take a stab at modeling our code. This way, we can skip a ton of the challenging thinking that will be required. Once that doesn’t work, we’ll have a go at generalizing properties out of conventional example-based cases, which will assist us when deciding the standards supporting our assumptions. Another methodology we will utilize is finding invariants to tighten up from trifling properties into a robust test suite. At last, we’ll carry out symmetric properties as a simple cheat code for some particular issues.
Modeling expects us to compose an easy implementation of our code, regardless of whether or not it is algorithmically wasteful. The model ought to be easy, to the point, and clearly right. You would then be able to improve the real system as much as you need, and, as long as the two implementations act the same way, there’s a decent possibility that the complicated one is just as great as the clearly right one but quicker. So, for code that does a conceptually straightforward thing, modeling is valuable.
Modeling will generally work as long as it is feasible to compose a similar program multiple times, meaning up to one of the executions is so simple that it is correct. On the other hand, it is not generally viable and sometimes unrealistic, which is why we wanted to find better properties. However, that is altogether harder than finding any property that is already able todemonstrate troublesomely and requires a strong understanding of the problem space. A decent stunt to find a property begins by composing a standard unit test and then abstracting it away. We can see the usual ways to concoct every single individual example and replace them with generators.
You can find a simple example regarding this below:
last_test() ->
?assert(-10 =:= lists:last([-10])),
?assert(6 =:= lists:last([2,3,4,6])),
?assert(4 =:= lists:last([5,4])).
Sometimes, it is not possible to describe a program or a function. For instance, the judgment that a meal is acceptable is abstract, and it would be easier to judge if there was a criterion in place. In this criteria, there can be many things. For example:
These elements have an impact on the decision of “Is the food acceptable or not?”.
Similarly, there can be a lot of small parts in a software system that should work properly to prove the correctness of the whole product, but we may be unable to test the quality of these small parts because it can be challenging to describe them. Therefore, in a software system, we can distinguish comparable conditions that should always remain true. These are called invariants.
Sometimes, one component depends on another component to succeed, and sometimes two bits of code perform in opposite ways (ex: Encoder and Decoder). Furthermore, there can be a chain of operations that can be made reversible:
At whatever point you have a reversible arrangement of activities that you can gather, you can write the briefest kind of property, symmetric properties. It is a trick of symmetric properties to test this chain of operations at once. If the initial input is the final output, then all operations in sequence are working correctly.
We can write the following property for the code that does encoding and decoding:
prop_symmetric() ->
?FORALL(Data, list({atom(), any()}),
Data =:= decode(encode(Data))).
Free Resources