Property Test Functions

There are two variants of functions that are used to execute a property test in Kotest: forAll and checkAll.

For All#

The first, forAll, accepts an n-arity function (a, ..., n) -> Boolean that tests the property. The test will pass if, for all input values, the function returns true.

class PropertyExample: StringSpec({
"String size" {
forAll<String, String> { a, b ->
(a + b).length == a.length + b.length
}
}
})

Notice that this functions accepts type parameters for the argument types, with arity up to 14. Kotest uses these type parameters to locate a generator which provides (generates) random values of a suitable type.

For example, forAll<String, Int, Boolean> { a, b, c -> } is a 3-arity property test where argument a is a random String, argument b is a random int, and argument c is a random boolean.

Check All#

The second, checkAll, accepts an n-arity function (a, ..., n) -> Unit in which you can simply execute assertions against the inputs. This approach will consider a test valid if no exceptions are thrown. Here is the same example again written in the equivalent way using checkAll.

class PropertyExample: StringSpec({
"String size" {
checkAll<String, String> { a, b ->
(a + b).length shouldHaveLength a.length + b.length
}
}
})

The second approach is more general purpose than returning a boolean, but the first approach is from the original haskell libraries that inspired this library.

Iterations#

By default, Kotest will run the property test 1000 times. We can easily customize this by specifying the iteration count when invoking the test method.

Let's say we want to run a test 10,000 times.

class PropertyExample: StringSpec({
"a many iterations test" {
checkAll<Double, Double>(10_000) { a, b ->
// test here
}
}
})

Specifying Generators#

You saw in the previous examples that Kotest would provide values automatically based on the type parameter(s). It does this by locating a generator that generates values for the required type.

For example, the automatically provided Integer generator generates random ints from all possible values - negative, positive, infinities, zero and so on.

This is fine for basic tests but often we want more control over the sample space. For example, we may want to test a function for numbers in a certain range only.

Then you would need to specify the generator(s) manually.

class PropertyExample: StringSpec({
"is allowed to drink in Chicago" {
forAll(Arb.int(21..150)) { a ->
isDrinkingAge(a) // assuming some function that calculates if we're old enough to drink
}
}
"is allowed to drink in London" {
forAll(Arb.int(18..150)) { a ->
isDrinkingAge(a) // assuming some function that calculates if we're old enough to drink
}
}
})

You can see we created two tests and in each test passed a generator into the forAll function with a suitable int range.

See here for a list of the built in generators.