Extras

kotlin-faker provides additional functionality outside of data generation from static .yml dictionaries.

ToC


Random instance of any class

It is possible to create a random instance of (almost) any class.

There are some rules to keep in mind:

  • By default, the constructor with the least number of arguments is used (This can be configured - read on.)
  • kolin.Array type in the constructor is not supported at the moment

Random instance generation is available through Faker().randomProvider:

class Foo(val a: String)
class Bar(val foo: Foo)

val foo: Foo = faker.randomProvider.randomClassInstance()
val bar: Bar = faker.randomProvider.randomClassInstance()


back-to-toc


Pre-Configuring type generation for constructor arguments

Some, or all, of the constructor params can be instantiated with values following some pre-configured logic using typeGenerator or namedParameterGenerator functions. Consider the following example:

class Baz(val id: Int, val uuid: UUID, val relatedUuid: UUID)

val baz: Baz = faker.randomProvider.randomClassInstance {
    typeGenerator<UUID> { UUID.fromString("00000000-0000-0000-0000-000000000000") }
    typeGenerator<Int> { 0 }
    namedParameterGenerator("relatedUuid") { UUID.fromString("11111111-1111-1111-1111-111111111111") }
}


So for each instance of Baz the following will be true:

baz.id == 0
baz.uuid == UUID.fromString("00000000-0000-0000-0000-000000000000")
baz.relatedUuid == UUID.fromString("11111111-1111-1111-1111-111111111111")

This example itself does not make that much sense, since we're using "static" values, but we could also do something like:

val baz: Baz = faker.randomProvider.randomClassInstance {
    typeGenerator<UUID> { UUID.randomUUID() }
}


...or even so:

class Person(val id: Int, val name: String)

val person: Person = faker.randomProvider.randomClassInstance {
    typeGenerator<String> { faker.name.fullName() }
}

back-to-toc


Deterministic constructor selection

By default, the constructor with the least number of args is used when creating a random instance of the class. This might not always be desirable and can be configured. Consider the following example:

class Foo
class Bar(val int: Int)
class Baz(val foo: Foo, val string: String)
class FooBarBaz {
    var foo: Foo? = null
        private set
    var bar: Bar? = null
        private set
    var baz: Baz? = null
        private set

    constructor(foo: Foo) {
        this.foo = foo
    }

    constructor(foo: Foo, bar: Bar) : this(foo) {
        this.bar = bar
    }

    constructor(foo: Foo, bar: Bar, baz: Baz) : this(foo, bar) {
        this.baz = baz
    }
}


If there is a need to use the constructor with 3 arguments when creating an instance of FooBarBaz, we can do it like so:

val fooBarBaz: FooBarBaz = faker.randomProvider.randomClassInstance {
    constructorParamSize = 3
    fallbackStrategy = FallbackStrategy.USE_MAX_NUM_OF_ARGS
}
assertNotEquals(fooBarBaz.foo, null)
assertNotEquals(fooBarBaz.bar, null)
assertNotEquals(fooBarBaz.baz, null)


In the above example, FooBarBaz will be instantiated with the first discovered constructor that has parameters.size == 3; if there are multiple constructors that satisfy this condition - a random one will be used. Failing that (for example, if there is no such constructor), a constructor with the maximum number of arguments will be used to create an instance of the class.

Alternatively to constructorParamSize, a constructorFilterStrategy config property can be used as well:

val fooBarBaz: FooBarBaz = faker.randomProvider.randomClassInstance {
    constructorFilterStrategy = ConstructorFilterStrategy.MAX_NUM_OF_ARGS
}
assertNotEquals(fooBarBaz.foo, null)
assertNotEquals(fooBarBaz.bar, null)
assertNotEquals(fooBarBaz.baz, null)

The above has the following rules:

  • constructorParamSize config property takes precedence over constructorFilterStrategy
  • both can be specified at the same time, though in most cases it probably makes more sense to use fallbackStrategy with constructorParamSize as it just makes things a bit more readable
  • configuration properties that are set in randomClassInstance block will be applied to all "children" classes. For example classes Foo, Bar, and Baz will use the same random instance configuration settings when instances of those classes are created in FooBarBaz class.

back-to-toc


Configuring the size of generated Collections

Support for kotlin.collections.Collection parameter types - List, Set and Map has been added in version 1.9.0 and with that - a new configuration parameter to configure the size of the generated collection.

By default, all collections will be generated with only 1 element:

class Foo(
    val list: List<String>,
    val set: Set<String>,
    val map: Map<String, Int>
)

val foo = faker.randomProvider.randomClassInstance<Foo>()

assertEquals(foo.list.size, 1)
assertEquals(foo.set.size, 1)
assertEquals(foo.map.size, 1)


This can be configured using collectionsSize parameter:

class Foo(
    val list: List<String>,
    val set: Set<String>,
    val map: Map<String, Int>
)

val foo = faker.randomProvider.randomClassInstance<Foo> {
    collectionsSize = 6
}

assertEquals(foo.list.size, 6)
assertEquals(foo.set.size, 6)
assertEquals(foo.map.size, 6)

info

Note that the collectionsSize configuration parameter affects all 3 types of Collections.


warn

It is also worth noting that typeGenerator<Foo> { ... } configuration, which was covered above, will not affect Foo typed elements in a generated collection.

Consider the following example. If typeGenerator<String> { "a string" } would affect String typed elements of Set, the resulting generated set would be of size 1:

class TestClass(
    val string: String,
    val set: Set<String>
)

val testClass = faker.randomProvider.randomClassInstance<TestClass> {
    typeGenerator { "a string" }
    collectionsSize = 10
}

assertEquals(testClass.string, "a string")
assertEquals(testClass.set.size, 10)


At the same time, typeGenerator configurator itself can be used with collections as well:

class Foo
class Bar(
    val list: List<Foo>,
    val set: Set<String>,
    val map: Map<String, Int>
)

val bar = faker.randomProvider.randomClassInstance<Bar> {
    typeGenerator { emptyList<Foo>() }
    typeGenerator { setOf("one", "two", "fortytwo") }
    typeGenerator { mapOf("pwd" to 12177) }
}
assertEquals(bar.list, emptyList<Foo>())
assertEquals(bar.set, setOf("one", "two", "fortytwo"))
assertEquals(bar.map, mapOf("pwd" to 12177))


back-to-toc


Random Everything

Faker provides its wrapper functions around java.util.Random (with some additional functionality that is not covered by java.util.Random) through Faker().random property.

Wrappers around java.util.Random

faker.random.nextBoolean()
faker.random.nextChar()
faker.random.nextDouble()
faker.random.nextFloat()
faker.random.nextInt()
faker.random.nextInt(bound = 100)
faker.random.nextInt(min = 100, max = 999)
faker.random.nextInt(intRange = (0..99))
faker.random.nextLetter(upper = false)

Random Enum Instance

enum class Foo {
    ONE,
    TWO,
    FORTY_TWO
}


faker.random.nextEnum<Foo>()
faker.random.nextEnum(enum = Foo::class.java)
faker.random.nextEnum(values = Foo.values())
faker.random.nextEnum(enum = Foo::class.java) { it != Foo.ONE }
faker.random.nextEnum<Foo>(excludeName = "ONE")

Random Strings

faker.random.randomString(
    length = 42,
    numericalChars = false
)


faker.random.randomString(
    length = 24,
    locale = Locale.forLanguageTag("nb-NO"),
    indexChars = true,
    auxiliaryChars = true,
    punctuationChars = true,
    numericalChars = true,
)

Random sub-lists and sub-sets

val list = List(100) { it }
faker.random.randomSublist(list, size = 10, shuffled = true)
faker.random.randomSublist(list, sizeRange = 6..42, shuffled = true)


val set = setOf(*List(100) { it }.toTypedArray())
faker.random.randomSubset(set, size = 10, shuffled = true)
faker.random.randomSubset(set, sizeRange = 66..99, shuffled = true)

Random element from a list/array

val list = listOf(1, 2, 3)
faker.random.randomValue(list)

Random UUID

faker.random.nextUUID()


back-to-toc


Random Strings from Templates

Faker's StringProvider allows for replacing certain user-defined parts of strings with randomly generated chars (letters and digits), as well as generating strings from regex expressions. The following functions are available withing the Faker().string:

faker.string.numerify("123###").all { it.isDigit() } shouldBe true
faker.string.letterify("foo???").all { it.isLetter() } shouldBe true
faker.string.letterify("???BAR", true).all { it.isUpperCase() } shouldBe true
faker.string.letterify("???bar", false).all { it.isLowerCase() } shouldBe true
faker.string.bothify("foo???bar###")
faker.string.regexify("""\d{42}""").all { it.isDigit() } shouldBe true
faker.string.regexify("""\d{42}""").length shouldBe 42


back-to-toc