Kotest Property Extension

TOC

About

Kotlin-faker kotest-property and kotest-property-ksp artifacts provide faker-based Arb generators extensions via KSP compiler plugin for kotest property testing.

Usage

Installation

kotest-property extension builds upon KSP, from which it inherits easy integration with Gradle. To use this extension, add the following in your build.gradle.kts:

  • ① add the ksp plugin (You can check the latest version in their releases.)
  • ② add the core kotlin-faker dependency to the test classpath
  • ③ add the testImplementation dependency for the kotest-property extension
  • ④ add the kspTest dependency for the kotest-property and kotest-property-ksp extensions
    • This will generate the code for test sources. If you're using kotlin-faker for something other than testing (e.g. data anonymization) and want to generate extension code for main source instead, use ksp configuration for this dependency.
  • ⑤ the core kotlin-faker dependency also needs to be added to kspTest configuration
plugins {
    id("com.google.devtools.ksp") version "$kspVersion" // ①
}

dependencies {
    testImplementation("io.github.serpro69:kotlin-faker:$fakerVersion") // ②
    testImplementation("io.github.serpro69:kotlin-faker-kotest-property:$fakerExtVersion") // ③
    kspTest("io.github.serpro69:kotlin-faker-kotest-property:$fakerVersion") // ④
    kspTest("io.github.serpro69:kotlin-faker-kotest-property-ksp:$fakerExtVersion") // ④
    kspTest("io.github.serpro69:kotlin-faker:$fakerVersion") // ⑤
}

back-to-toc


Generate Arb Extensions

① To generate Arb extensions for Fakers, use the FakerArb annotation on the test file and provide the "Faker" classes to generate extensions for:

@file:FakerArb(Faker::class, BooksFaker::class, EduFaker::class) // ①

package com.example
info

The annotation only needs to be used once, and you can even use it in a separate empty file in your test sources


warn

For any additional fakers that you want to generate Arbs for, e.g. BooksFaker or EduFaker, make sure to add the corresponding dependency to both testImplementation and kspTest configurations:

dependencies {
    testImplementation("io.github.serpro69:kotlin-faker-books:$fakerVersion")
    testImplementation("io.github.serpro69:kotlin-faker-edu:$fakerVersion")
    kspTest("io.github.serpro69:kotlin-faker-books:$fakerVersion")
    kspTest("io.github.serpro69:kotlin-faker-edu:$fakerVersion")
}


warn

Note that if you're using Faker BOM to manage faker versions, ksp and kspTest configurations are not able to pick up versions from the bom, and they (versions) need to be declared explicitly for these configurations.

See more in the ksp/issues/1884.

dependencies {
    testImplementation(platform("io.github.serpro69:kotlin-faker-bom:2.0.0-rc.4"))
    testImplementation("io.github.serpro69:kotlin-faker") // uses version from the bom
    testImplementation("io.github.serpro69:kotlin-faker-kotest-property") // uses version from the bom
-   kspTest("io.github.serpro69:kotlin-faker-kotest-property") // CAN'T use version from the bom
-   kspTest("io.github.serpro69:kotlin-faker-kotest-property-ksp")
-   kspTest("io.github.serpro69:kotlin-faker")
+   kspTest("io.github.serpro69:kotlin-faker-kotest-property:2.0.0-rc.1")
+   kspTest("io.github.serpro69:kotlin-faker-kotest-property-ksp:2.0.0-rc.1")
+   kspTest("io.github.serpro69:kotlin-faker:2.0.0-rc.4")
}


The plugin will generate Arb generator extensions for all specified faker classes and their data providers.

① Each Faker instance will have an arb property that provides access to standard faker data generators, but which return data wrapped in Arb instances.

② Generated code will also include faker extension properties for Arb.Companion that exposes the same functionality.

③ Each generated ArbFaker instance will include all the standard Faker data generator properties, i.e. address, color, currency, etc. for the "core" Faker.

④ Each Arb-based data provider, e.g. ArbAddress that "implements" Address data provider, will include all functions for that given data provider, but returned as parameterized Arb types.

public val Faker.arb: ArbFaker // ①
    get() = ArbFaker(this)
public val Arb.Companion.faker: ArbFaker // ②
    get() = io.github.serpro69.kfaker.ArbFaker(Faker())

public val BooksFaker.arb: ArbBooksFaker // ①
    get() = ArbBooksFaker(this)
public val Arb.Companion.booksFaker: ArbBooksFaker // ②
    get() = io.github.serpro69.kfaker.books.ArbBooksFaker(BooksFaker())

public class ArbFaker(private val faker: Faker) { // ③
    public val address: ArbAddress by lazy { ArbAddress(faker.address) }

    public val color: ArbColor by lazy { ArbColor(faker.color) }

    public val currency: ArbCurrency by lazy { ArbCurrency(faker.currency) }

    // ...
}

public class ArbAddress internal constructor(private val address: Address) { // ④
    public fun city(): Arb<String> = arbitrary { address.city() }

    public fun country(): Arb<String> = arbitrary { address.country() }

    // ...
}


This can then be used with standard Kotest property testing functionality, just like the built-in Arbs, e.g. with property test functions like forAll:

package com.example

import io.github.serpro69.kfaker.Faker
import io.github.serpro69.kfaker.arb
import io.github.serpro69.kfaker.books.BooksFaker
import io.github.serpro69.kfaker.books.arb
import io.github.serpro69.kfaker.books.booksFaker
import io.github.serpro69.kfaker.edu.EduFaker
import io.github.serpro69.kfaker.edu.arb
import io.github.serpro69.kfaker.faker
import io.github.serpro69.kfaker.kotest.FakerArb
import io.github.serpro69.kfaker.randomClass
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.property.Arb
import io.kotest.property.forAll

class KotestPropertyArbsTest : DescribeSpec({
    describe("Custom kotlin-faker Arbs") {
        it("should generate quotes from the bible") {
            val b = BooksFaker()
            forAll(b.arb.bible.quote()) { q: String ->
                q.isNotBlank()
            }
        }
        it("should generate addresses") {
            val f = Faker()
            forAll(f.arb.address.city()) { q ->
                q.isNotBlank()
            }
            forAll(f.arb.address.city(), f.arb.address.streetName()) { city, street ->
                city.isNotBlank()
                street.isNotBlank()
            }
        }
        it("should generate quotes from companion object") {
            forAll(Arb.booksFaker.bible.quote()) { q: String ->
                q.isNotBlank()
            }
        }
        it("should generate addresses from companion object") {
            class Address(val city: String, val state: String) {
                fun isValid() = city.isNotBlank() && state.isNotBlank()
            }
            forAll(Arb.faker.address.city(), Arb.faker.address.state()) { city, state ->
                Address(city, state).isValid()
            }
        }
    }
})

back-to-toc


Random Class Instance ARB

The kotlin-faker-kotest-property extension additionally adds a randomClass extension property to Arb.Compaion for generating a random instance of any class, which provides the same functionality as the default Random Class Instance faker functionality, but wrapped in Arb type to be used with kotest property testing.

it("should generate person with address") {
    val f = Faker()
    val person: () -> Arb<Person> = {
        Arb.randomClass.instance<Person> {
            namedParameterGenerator("name") { f.name.name() }
            namedParameterGenerator("age") { f.random.nextInt(20, 30) }
        }
    }
    val address: () -> Arb<Address> = {
        Arb.randomClass.instance<Address> {
            namedParameterGenerator("city") { f.address.city() }
            namedParameterGenerator("streetName") { f.address.streetName() }
            namedParameterGenerator("streetAddress") { f.address.streetAddress() }
        }
    }
    forAll(person(), address()) { p: Person, a: Address ->
        p.name.isNotBlank()
        p.age in 20..30
        a.city.isNotBlank()
        a.streetName.isNotBlank()
        a.streetAddress.isNotBlank()
    }
}
info

If you only need randomClass instance extension, it is sufficient to just add "io.github.serpro69:kotlin-faker-kotest-property:$fakerExtVersion" dependency to e.g. testImplementation configuration, as it is part of the library itself and is not auto-generated by KSP.


back-to-toc