Random class instance¶
It is possible to create a random instance of (almost) any class.
There are some rules to keep in mind:
- A class must have a public or internal constructor
 - By default, a constructor with the least number of arguments is used (This can be configured - read on.)
 kolin.Arraytype in the constructor is not supported at the moment- Inner classes (either direct generation or as class parameter type) are not supported at the moment
 
Random instance generation is available through Faker().randomProvider:
Random Class Instance Configuration¶
Random Class Instance configuration can be applied on several levels. Consider the following classes:
Configuration via FakerConfig¶
 This takes the least precedence and applies to all instances (see Making a copy/new instance of RandomClassProvider) of RandomClassProvider if set.
val cfg = fakerConfig {
    randomClassInstance {
        typeGenerator<Bar> {
            Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111"))
        }
    }
}
val f = Faker(cfg)
val baz: Baz = f.randomClass.randomClassInstance<Baz>()
assertEquals(
    baz.bar,
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
)
val anotherBaz = f.randomClass.new().randomClassInstance<Baz>()
assertEquals(
    anotherBaz.bar,
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
)
Configuration via Faker#randomProvider¶
 This takes higher precedence and will also merge any configuration that was set on the previous level.
val cfg = fakerConfig {
    randomClassInstance {
        typeGenerator<Bar> {
            Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111"))
        }
    }
}
val f =
    Faker(cfg).also {
        it.randomClass.configure {
            typeGenerator<UUID> {
                UUID.fromString("00000000-0000-0000-0000-000000000000")
            }
        }
    }
val bar: Bar = f.randomClass.randomClassInstance()
val baz: Baz = f.randomClass.randomClassInstance()
assertEquals(bar.uuid, UUID.fromString("00000000-0000-0000-0000-000000000000"))
assertEquals(
    baz.bar,
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
)
Configuration via randomClassInstance function¶
 This configuration takes the most precedence and does not take into account configurations applied on other levels.
faker.randomClass.configure {
    typeGenerator<Bar> {
        Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111"))
    }
}
val baz: Baz =
    faker.randomClass.randomClassInstance {
        typeGenerator<Bar> {
            Bar(1, UUID.fromString("00000000-0000-0000-0000-000000000000"))
        }
    }
assertEquals(
    baz.bar,
    Bar(1, UUID.fromString("00000000-0000-0000-0000-000000000000")),
)
Pre-Configuring type generation¶
Predefined types for constructor parameters¶
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 user: String)
val baz: Baz =
    faker.randomClass.randomClassInstance {
        typeGenerator<UUID> {
            UUID.fromString("00000000-0000-0000-0000-000000000000")
        }
        typeGenerator<Int> { 0 }
        typeGenerator<String> { parameterInfo ->
            "${parameterInfo.name}_${randomString()}"
        }
        namedParameterGenerator("relatedUuid") {
            UUID.fromString("11111111-1111-1111-1111-111111111111")
        }
    }
So for each instance of Baz the following will be true:
This example itself does not make that much sense, since we're using "static" values, but we could also do something like:
...or even so:
Pre-defined instance for classes with no public or internal constructors¶
By default, randomClassInstance can't generate classes with no public constructors, but this can be worked around by using typeGenerator:
You could also use the same approach to create interfaces, for example:
interface TestInterface {
  val id: Int
  val name: String
}
val testInterface = randomProvider.randomClassInstance<TestInterface> {
  typeGenerator<TestInterface> {
    object : TestInterface {
      override val id: Int = 42
      override val name: String = "Deep Thought"
    }
  }
}
testInterface.id shouldBe 42
testInterface.name shouldBe "Deep Thought"
A random class instance will be generated using the following precedence rules:
- object instance
 - "default instance" of a class
 - uses a public/internal constructor with the least number of arguments, unless otherwise configured (see Random Class Instance Configuration)
 - "predefined instance" of a class if no public or internal constructors are found
 - failing all of the above, 
NoSuchElementExceptionwill be thrown 
Predefined collection element types¶
It may be desirable to define how elements of a Collection (currently supports Lists and Sets) constructor parameter type are generated, for this collectionElementTypeGenerator function can be used:
fun randomListString() = "list"
fun randomString() = "string"
class Baz(val list: List<String>, val set: Set<String>)
val baz: Baz =
    faker.randomClass.randomClassInstance {
        collectionElementTypeGenerator<String> {
            // customize generators for different collection types
            if ((it.type.classifier as KClass<*>) == List::class) {
                // generate random string elements for parameters of
                // List<String> type
                randomListString()
            } else {
                // generate random string elements for parameters of Set
                // type
                randomString()
            }
        }
    }
So for each instance of Baz the following will be true:
This example kind of makes little sense, since we're using "static" values, so it's just for example purposes.
Info
Note how we can use it.type.classifier to figure out the parameter classifier and further customize generation for different collection types. 
 Explore other properties of it, which exposes more details about the parameter that is being customized.
There are also two similar methods for map entries: mapEntryKeyTypeGenerator and mapEntryValueTypeGenerator, which can be used to configure how keys and values are generated in Map constructor parameter types:
Nullable collection element types are also supported (but note that nulls as values are never returned):
data class Nullable(
    val ints: List<Int?>,
    val longs: Set<Long?>,
    val map: Map<Char?, String?>,
)
val nullable =
    faker.randomClass.randomClassInstance<Nullable> {
        collectionsSize = 10
        collectionElementTypeGenerator<Int?> {
            if (faker.random.nextBoolean()) null else 42
        }
        collectionElementTypeGenerator<Long?> {
            if (!faker.random.nextBoolean()) 0L else null
        }
        mapEntryKeyTypeGenerator<Char> {
            faker.random.randomValue(listOf('a', 'b', 'c', 'd', 'e', 'f'))
        }
        mapEntryValueTypeGenerator<String?> {
            if (faker.random.nextBoolean()) null else "foo"
        }
    }
nullable.ints shouldContain 42
// we allow nullable values, but `null` as a value will never be returned
nullable.ints shouldNotContain null
// with above config, if nextBoolean returns false, we say "return null",
// but since nulls are never returned as value, all nulls will be returned
// as random instance,
// hence we won't have all 42's
nullable.ints shouldNot containOnly(42)
nullable.longs shouldContain 0L
nullable.longs shouldNotContain null
nullable.longs shouldNot containOnly(0L)
nullable.map.keys shouldNotContain null
nullable.map.keys shouldContainAnyOf listOf('a', 'b', 'c', 'd', 'e', 'f')
nullable.map.values shouldNotContain null
nullable.map.values shouldContain "foo"
nullable.map.values shouldNot containOnly("foo")
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:
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:
The above has the following rules:
constructorParamSizeconfig property takes precedence overconstructorFilterStrategy- both can be specified at the same time, though in most cases it probably makes more sense to use 
fallbackStrategywithconstructorParamSizeas it just makes things a bit more readable - configuration properties that are set in 
randomClassInstanceblock will be applied to all "children" classes. For example classesFoo,Bar, andBazwill use the same random instance configuration settings when instances of those classes are created inFooBarBazclass. 
Default values selection¶
By default, all parameters of a selected constructor would be generated with randomized values:
class Foo(val i: Int)
class TestClass(
    val iMin: Int = Int.MIN_VALUE,
    val iMax: Int = Int.MAX_VALUE,
    val s: String = "sometimes a string... is just a string",
    val foo: Foo = Foo(369),
)
val testClass: TestClass = Faker().randomClass.randomClassInstance()
assertNotEquals(Int.MIN_VALUE, testClass.iMin)
assertNotEquals(Int.MAX_VALUE, testClass.iMax)
assertNotEquals("sometimes a string... is just a string", testClass.s)
assertNotEquals(369, testClass.foo.i)
This behavior can be changed with defaultValuesStrategy configuration option.
One can choose between generating values only for non-optional constructor parameters:
...or randomly selecting between a default value and a randomly-generated one:
it("should randomly pick a default or a random value") {
    val testClass: TestClass =
        Faker().randomClass.randomClassInstance {
            defaultValuesStrategy = PICK_RANDOMLY
        }
    assert(
        testClass.iMin == Int.MIN_VALUE ||
            testClass.iMax == Int.MAX_VALUE ||
            testClass.s == "sometimes a string... is just a string" ||
            testClass.foo.i == 369
    )
    assert(
        !(testClass.iMin == Int.MIN_VALUE &&
            testClass.iMax == Int.MAX_VALUE &&
            testClass.s == "sometimes a string... is just a string" &&
            testClass.foo.i == 369)
    )
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:
This can be configured using collectionsSize parameter:
Info
Note that the collectionsSize configuration parameter affects all 3 types of Collections.
Warning
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:
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.randomClass.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))
Making a new instance of Random Class Provider¶
RandomClassProvider has two functions: new and copy, that allow you to create another instance of the class, for example, a one that has a different type generation configuration.
New Instance¶
To make a new instance of randomProvider:
val cfg = fakerConfig {
    randomClassInstance { // ❶
        typeGenerator<Bar> {
            Bar(1, UUID.fromString("00000000-0000-0000-0000-000000000000"))
        }
    }
}
val f = Faker(cfg)
f.randomClass.configure { // ❷
    typeGenerator<Bar> {
        Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111"))
    }
}
val new = f.randomClass.new() // ❸
val baz: Baz = f.randomClass.randomClassInstance<Baz>()
val newBaz: Baz = new.randomClassInstance<Baz>()
assertEquals(
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
    baz.bar,
)
assertEquals(
    Bar(1, UUID.fromString("00000000-0000-0000-0000-000000000000")),
    newBaz.bar,
)
Info
Any configuration set via fakerConfig ( ❶ ), will be applied to the new instance ( ❸ ) as well. 
 Any configuration set via faker.randomProvider instance ( ❷ ) is NOT applied to the new instance.
Instance Copy¶
To make a copy of an existing instance of randomProvider:
val cfg = fakerConfig { // ❶
    randomClassInstance {
        typeGenerator<Bar> {
            Bar(1, UUID.fromString("00000000-0000-0000-0000-000000000000"))
        }
    }
}
val f = Faker(cfg)
f.randomClass.configure { // ❷
    typeGenerator<Bar> {
        Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111"))
    }
}
val copy = f.randomClass.copy() // ❸
val baz: Baz = f.randomClass.randomClassInstance<Baz>()
val bazCopy: Baz = copy.randomClassInstance<Baz>()
assertEquals(
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
    baz.bar,
)
assertEquals(
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
    bazCopy.bar,
)
copy.configure { // ❹
    typeGenerator<Bar> {
        Bar(0, UUID.fromString("22222222-2222-2222-2222-222222222222"))
    }
}
val originalBaz: Baz = f.randomClass.randomClassInstance<Baz>()
val reconfiguredBazCopy = copy.randomClassInstance<Baz>()
assertEquals(
    Bar(42, UUID.fromString("11111111-1111-1111-1111-111111111111")),
    originalBaz.bar,
)
assertEquals(
    Bar(0, UUID.fromString("22222222-2222-2222-2222-222222222222")),
    reconfiguredBazCopy.bar,
)
Info
Any configuration that was already applied to faker.randomProvider ( ❶ and ❷ ), will be applied to the copy ( ❸ ) as well. 
 The copy, just as new instance, can of course be reconfigured ( ❹ ) as needed, which does not affect the configuration of the faker.randomProvider or configurations of other "copies".
Dealing with Generic Types¶
Generic parameter types are not fully supported at this moment due to type-erasure on the JVM (See also https://github.com/serpro69/kotlin-faker/issues/191)
Top-level functions¶
Sometimes you just want to generate random POJO instances and don't need the whole range of kotlin-faker functionality? For these cases you can import randomClassInstance top-level function and use it directly w/o the need to instantiate an instance of Faker
It can also be configured via configurator lambda parameter, or via FakerConfig instance:
import io.github.serpro69.kfaker.fakerConfig
import io.github.serpro69.kfaker.randomClassInstance
import java.time.Instant
val randomClass = randomClassInstance<MyClass>() {
  typeGenerator<Instant> { Instant.now() }
}
val cfg = fakerConfig {
  randomClassInstance {
    typeGenerator<Instant> { Instant.MIN }
  }
}
val another = randomClassInstance<MyClass>(cfg)