Skip to main content

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 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.