Property based testing in Java with kqwik

There are a couple (maybe more) libraries for property based testing, I found that jqwik worked well for me.

I’m not going to go too deep into what property based testing is, except to state that for my purposes, property based testing allows me to generate random values to pass into my tests. I can generate multiple “tries” or items of data and even restrict ranges of values.

Specifically I’m using property based testing to generate values for some unit conversion code, the values get converted to a different unit and back – if the resultant value is the same as the input value we can assume that the conversion back and forth works correctly.

Anyway there are plenty of resources to read on the subject, so let’s get stuck into some code.

First off we need the following dependency to our pom.xml (obviously change the version to suit)

<dependency>
  <groupId>net.jqwik</groupId>
  <artifactId>jqwik</artifactId>
  <version>1.6.2</version>
  <scope>test</scope>
</dependency>

Let’s look a an example unit test

@Property(tries = 100)
public void testFromDegreesToGradiansAndBack(@ForAll @DoubleRange(min = -1E12, max = 1E12) double value) {
  final double convertTo = Angle.Degrees.toGradians(value);
  final double convertBack = Angle.Gradians.toDegrees(convertTo);
  assertEquals(value, convertBack, 0.01);
}

The @Property annotation tells the jqik runner to supply data for the method, creating 100 items of data (or tries) and @ForAll is used to assign data to the double value. Finally for this test I wanted to constrain the range of double values to [-1E12, 1E12].

The output (upon success) looks similar to this

timestamp = 2022-01-05T20:00:08.038391600, AngleTests:testFromDegreesToGradiansAndBack = 
                              |--------------------jqwik--------------------
tries = 100                   | # of calls to property
checks = 100                  | # of not rejected calls
generation = RANDOMIZED       | parameters are randomly generated
after-failure = PREVIOUS_SEED | use the previous seed
when-fixed-seed = ALLOW       | fixing the random seed is allowed
edge-cases#mode = MIXIN       | edge cases are mixed in
edge-cases#total = 7          | # of all combined edge cases
edge-cases#tried = 6          | # of edge cases tried in current run
seed = -3195058119397656120   | random seed to reproduce generated values

Errors will cause the test to fail and output the failure for you to review.