1. Overview
In this article, we’ll be looking at the concept of Property Testing and its implementation in the javaslang-test library.
The Property based testing (PBT) allows us to specify the high-level behavior of a program regarding invariants it should adhere to.
2. What is Property Testing?
A property is the combination of an invariant with an input values generator. For each generated value, the invariant is treated as a predicate and checked whether it yields true or false for that value.
As soon as there is one value which yields false, the property is said to be falsified, and checking is aborted. If a property cannot be invalidated after a specific amount of sample data, the property is assumed to be satisfied.
Thanks to that behavior, our test fail-fast if a condition is not satisfied without doing unnecessary work.
3. Maven Dependency
First, we need to add a Maven dependency to the javaslang-test library:
<dependency> <groupId>io.javaslang</groupId> <artifactId>javaslang-test</artifactId> <version>${javaslang.test.version}</version> </dependency> <properties> <javaslang.test.version>2.0.5</javaslang.test.version> </properties>
4. Writing Property Based Tests
Let’s consider a function that returns a stream of strings. It is an infinite stream of 0 upwards that maps numbers to the strings based on the simple rule. We are using here an interesting Javaslang feature called the Pattern Matching:
private static Predicate<Integer> divisibleByTwo = i -> i % 2 == 0; private static Predicate<Integer> divisibleByFive = i -> i % 5 == 0; private Stream<String> stringsSupplier() { return Stream.from(0).map(i -> Match(i).of( Case($(divisibleByFive.and(divisibleByTwo)), "DividedByTwoAndFiveWithoutRemainder"), Case($(divisibleByFive), "DividedByFiveWithoutRemainder"), Case($(divisibleByTwo), "DividedByTwoWithoutRemainder"), Case($(), ""))); }
Writing the unit test for such method will be error prone because there is a high probability that we’ll forget about some edge case and basically not cover all possible scenarios.
Fortunately, we can write a property-based test that will cover all edge cases for us. First, we need to define which kind of numbers should be an input for our test:
Arbitrary<Integer> multiplesOf2 = Arbitrary.integer() .filter(i -> i > 0) .filter(i -> i % 2 == 0 && i % 5 != 0);
We specified that input number needs to fulfill two conditions – it needs to be greater that zero, and needs to be dividable by two without remainder but not by five.
Next, we need to define a condition that checks if a function that is tested returns proper value for given argument:
CheckedFunction1<Integer, Boolean> mustEquals = i -> stringsSupplier().get(i).equals("DividedByTwoWithoutRemainder");
To start a property-based test, we need to use the Property class:
CheckResult result = Property .def("Every second element must equal to DividedByTwoWithoutRemainder") .forAll(multiplesOf2) .suchThat(mustEquals) .check(10_000, 100); result.assertIsSatisfied();
We’re specifying that, for all arbitrary integers that are multiples of 2, the mustEquals predicate must be satisfied. The check() method takes a size of a generated input and number of times that this test will be run.
We can quickly write another test that will verify if the stringsSupplier() function returns a DividedByTwoAndFiveWithoutRemainder string for every input number that is divisible by two and five without the remainder.
The Arbitrary supplier and CheckedFunction need to be changed:
Arbitrary<Integer> multiplesOf5 = Arbitrary.integer() .filter(i -> i > 0) .filter(i -> i % 5 == 0 && i % 2 == 0); CheckedFunction1<Integer, Boolean> mustEquals = i -> stringsSupplier().get(i).endsWith("DividedByTwoAndFiveWithoutRemainder");
Then we can run the property-based test for one thousand iterations:
Property.def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder") .forAll(multiplesOf5) .suchThat(mustEquals) .check(10_000, 1_000) .assertIsSatisfied();
5. Conclusion
In this quick article, we had a look at the concept of property based testing.
We created tests using the javaslang-test library; we used the Arbitrary, CheckedFunction, and Property class to define property-based test using javaslang-test.
The implementation of all these examples and code snippets can be found over on GitHub – this is a Maven project, so it should be easy to import and run as it is.