Kotest Property Based Testing
Fixture Monkey’s Kotest plugin introduces two primary features that enhance property-based testing within the Kotest framework: forAll
and checkAll
.
The KotestPlugin and KotlinPlugin should be added to enable this feature.
val fixtureMonkey: FixtureMonkey = FixtureMonkey.builder()
.plugin(KotestPlugin())
.plugin(KotlinPlugin())
.build()
ForAll
Kotest provides a forAll
function that accepts an n-arity function (a, ..., n) -> Boolean
to test a property.
The test passes if, for all input values, the function returns true.
This function accepts type parameters for the argument types, which Kotest uses to locate a generator that provides random values of a suitable type.
class PropertyExample: StringSpec({
"String size" {
forAll<String, String> { a, b ->
(a + b).length == a.length + b.length
}
}
})
For cases when a custom generator is needed, it’s possible to specify generators (called Arb
in Kotest).
However, only generators of limited types are provided with kotest, and it is hard to customize.
Fixture Monkey offers a way to generate Arb
for custom types using the giveMeArb()
function.
You can further customize the generator using Fixture Monkey’s customization APIs.
Here’s an example of performing property-based testing with forAll
using Fixture Monkey:
class KotestInKotestTest : StringSpec({
"forAll" {
forAll(fixtureMonkey.giveMeArb<StringObject> { it.set("value", "test") }) { a ->
a.value == "test"
}
}
}) {
data class StringObject(val value: String)
}
CheckAll
Fixture Monkey also provides the extension function checkAll
similar to Kotest’s checkAll
.
Primitive Type Input
With checkAll, you can test assertions against primitive data types, as shown in the example below:
class Test : StringSpec({
"checkAll" {
SUT.checkAll { string: String, int: Int ->
string shouldNotBeSameInstanceAs int
string shouldBe string
}
}
})
Custom Type Input
Fixture Monkey’s checkAll
extension function goes beyond primitive types.
You can also use custom types as input data, generated with Fixture Monkey.
class Test : StringSpec({
"checkAllObject" {
SUT.checkAll { stringObject: StringObject ->
stringObject.value shouldNotBe null
}
}
}) {
data class StringObject(val value: String)
}
ArbitraryBuilder Input
Additionally, you can work with ArbitraryBuilder instances and further customize them to execute assertions.
class Test : StringSpec({
"checkAllArbitraryBuilder" {
SUT.checkAll { string: ArbitraryBuilder<List<String>> ->
string
.size("$", 3)
.sample() shouldHaveSize 3
}
}
}) {
data class StringObject(val value: String)
}