Property based testing for kotlintest ported from quickcheck
Never write test data by hand again. Generate it!
The latest version 0.10.x
includes major changes and is experimental. I will write a migration guide at soon when I consider it stable, but up untill then I'd suggest sticking to 0.9.6
.
Add the following to your build.gradle
:
repositories {
maven { url 'https://dl.bintray.com/jannis/propCheck-kt' }
}
dependencies {
testImplementation 'propCheck:propCheck-kt:0.9.6'
}
Example usage:
propCheck {
forAll { (a, b): Pair<Int, Int> ->
a + b == b + a
}
}
// prints =>
+++ OK, passed 100 tests.
In this example propCheck ran 100 tests with random pairs of integers and checks if addition over integers is commutative.
propCheck
can also take an argument of type Args
to change the way tests are run:
propCheck(Args(maxSuccess = 300)) {
forAll { (a, b): Pair<Int, Int> ->
a + b == b + a
}
}
// prints =>
+++ OK, passed 300 tests.
A full overview of what can be customised can be seen here
A powerful concept in property based testing is shrinking. Given a failed test a shrinking function can output several "smaller" examples. For example: Lists tend to shrink to smaller lists, numbers shrink towards zero and so on. This often ends you with a minimal counterexample for the failed test and is very useful if the data would otherwise be messy (as will likely happen with random data).
For most cases enabling shrinking is as easy as changing forAll to forAllShrink:
propCheck {
forAllShrink { i: Int ->
i < 20
}
}
// prints =>
*** Failed! (after 35 tests and 3 shrinks):
Falsifiable
20
The quality of a property-based test is directly related to the quality of the data fed to it. There are some helpers to test and assure that the generated data holds some invariants.
To inspect results use either label, collect, classify or tabulate.
Here is an example on how to use classify:
propCheck {
forAll(OrderedList.arbitrary(Int.order(), Int.arbitrary())) { (l): OrderedList<Int> ->
classify(
l.size > 1,
"non-trivial",
l.shuffled().sorted() == l
)
}
}
// prints something like this =>
+++ OK, passed 100 tests (92,00% non-trivial).
To fail a test with insufficient coverage use checkCoverage with functions like cover or coverTable.
propCheck {
forAll(OrderedList.arbitrary(Int.order(), Int.arbitrary())) { (l): OrderedList<Int> ->
checkCoverage(
cover(
95.0,
l.size > 1,
"non-trivial",
l.shuffled().sorted() == l
)
)
}
}
// prints something like this =>
*** Failed! (after 800 tests):
Insufficient coverage
89,99% non-trivial
Here a coverage of 95% non-trivial lists is required, but only 89.99% could be reached.
propCheck is by itself stand-alone and does not provide test-runner capabilites like kotlintest. It however can and should be used together with a test-runner. By default propCheck
will throw exceptions on failure and thus will cause the test case it is being run in to fail. (That can be diasbled by using methods like propCheckWithResult
instead)
The following example runs a test in a kotlintest test:
class TestSpec : StringSpec({
"test if positive is always > 0!" {
propCheck {
forAll { (i): Positive<Int> ->
i > 0
}
}
}
"other tests" { ... }
})
There are plans of adding better support for test runners so that you can drop the propCheck {} wrapper.
First of all: If you are using arrow-kt: Great! There are plenty of instances already defined for arrows data-types.
If not then don't worry. Most of the api can be used without ever touching upon arrows datatypes and there are overloads specifically to avoid those cases if they do come up.
That is excluding types like Option or TupleN, which should be fine.
Another library that offers property-based testing is kotlintest, however it has several drawbacks:
Gen.map(id)
should not change the datatype, in kotlintest it will, because it forgets the shrinkerHowever propCheck also has some drawbacks:
Outside of property based testing kotlintest still makes a fine testing library, and I recommend using it to execute the tests and for anything that is not a property test!
propCheck
is still in its early days so if you notice bugs or think something can be improved please create issues or shoot me a pull request. All feedback is highly appreciated.
propCheck
is a port of the awesome library quickcheck. If you ever come around to use haskell make sure to give it a go!
Writing propCheck
was also made much easier by using arrow-kt to be able to write code in a similar style to haskell and thus close to the original.
As with quickCheck
make sure to check out arrow
for a more functional programming style in kotlin.