Kotest Property-based Testing
Property-based Testing
Kotest is split into several independently usable subprojects. One of these subprojects is the property-based testing framework. As expected, you do not have to use Kotest as your test framework to benefit from its property testing support.
What Is Property Testing?
Developers usually write example-based tests. These are the familiar unit tests you know and use: provide some input values and expected results, and a test framework such as Kotest or JUnit checks whether the actual result matches the expectation.
One problem with this approach is that it is easy to miss errors because of edge cases the developer did not think of, or because the selected inputs do not provide enough coverage. With property testing, hundreds or thousands of values are fed into the same test, and those values are usually generated randomly by the property testing framework.
For example, a good property testing framework includes values such as negative infinity, empty lists, and strings containing non-ASCII characters. These are the kinds of cases we often forget when writing example-based tests.
Property testing was originally devised in frameworks such as QuickCheck as a way to test properties of an object, that is, properties that must be true for every input. In other words, invariants. As an example of an invariant, given two strings a and b, length(a + b) should always equal length(a) + length(b).
This is where the term property testing comes from.
However, property testing is not limited to things like monad laws or basic numeric functions. Any test that can benefit from a wide range of input values is a good candidate. For example, if you have a function that validates usernames and you want to test that valid emails are accepted, property testing is useful because it can generate thousands of combinations that strengthen the validation logic.
Getting Started
The property testing framework is supported on all targets.
JVM/Gradle
Add the following dependency to your build:
dependencies {
testImplementation("io.kotest:kotest-property:$version")
}
JVM/Maven
Add the following dependency to your build:
<dependency>
<groupId>io.kotest</groupId>
<artifactId>kotest-property-jvm</artifactId>
<version>${version}</version>
<scope>test</scope>
</dependency>
Multiplatform
Add the following dependency to the commonTest source set:
kotlin {
sourceSets {
val commonTest by getting {
dependencies {
implementation("io.kotest:kotest-property:$version")
}
}
}
}
You can also add dependencies to a specific target. For example, you can add a dependency used only by the jsTest source set for the JavaScript target.
kotlin {
targets {
js {
browser()
nodejs()
}
}
sourceSets {
val jsTest by getting {
dependencies {
implementation("io.kotest:kotest-property:$version")
}
}
}
}
Next Steps
To generate input values for tests, Kotest uses the term generator. One generator per input argument is passed to the test function, and the test runs for the configured number of iterations.
Learn more about:
- How test functions are used.
- The different types of generators and their operations.
- How to write custom generators.
- How to specify configuration for tests, including seeds.