Repository: serpro69/kotlin-faker (kotlin)

Issue:
Allow RandomClassProvider::randomClassInstance to use predefinedGenerators from config if present
When generating a random object of type T via the `randomProvider.randomClassInstance<T>()` method, if a typeGenerator is defined for T then it would be helpful if `randomClassInstance` simply used that generator instead of ignoring it

### Example
I want to generate a random `Instant` (which does not have a public constructor) using a configured typeGenerator<Instant>:
```
val f = Faker()
f.randomProvider.configure {
    typeGenerator<Instant> { Instant.now() }
}
``` 
In this case `f.randomProvider.randomClassInstance<Instant>()` throws `java.util.NoSuchElementException: No suitable constructor found for class java.time.Instant` because the implementation of `randomClassInstance<Instant>()` only calls `KClass<T>.predefinedTypeOrNull` for the types of constructor args (of which there are none in the case of `Instant`)

This is not an issue when the the type of the predefined generator is not the type specified in `randomClassInstance<Instant>()`, for example if I define `data class Foo(val instant: Instant)`, then `f.randomProvider.randomClassInstance<Foo>()` generates correctly

### Possible solution
I propose adding a line to the beginning of `RandomClassProvider`'s `KClass<T>.randomClassInstance(config: RandomProviderConfig)` method that attempts to get a random instance from the predefined generators, and then return if the predefined generators return something. 
I am not sure how this would be best implemented, as the generators are designed to work with KParameters. One kinda hacky idea is to define something like:
```
fun KClass<*>.toParameterInfo() = ParameterInfo(
    index = 0,
    name = jvmName,
    isOptional = false,
    isVararg = false,
)
```
which would allow `val predefinedInstance = predefinedTypeOrNull(config, toParameterInfo()) as? T?` above line 105 of RandomClassProvider.kt and then the return statement on line 117 could become `return predefinedInstance ?: defaultInstance ?: objectInstance ?: run {`


Is this a change you would be interested in? And if so you have any suggestions on how best it could be implemented?

Pull request:
Closes #201 

Still not sure what the precedence should be, but I think it makes more sense to first try a default instance with public constructor. But I could probably as easy argue for the other way around , so I'm really just winging it here :D

Patch:
diff --git a/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/ParameterInfo.kt b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/ParameterInfo.kt
index 299772161c..c83a08db30 100644
--- a/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/ParameterInfo.kt
+++ b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/ParameterInfo.kt
@@ -3,9 +3,9 @@ package io.github.serpro69.kfaker.provider.misc
 import kotlin.reflect.KParameter
 
 /**
- * Provides additional informations about Class parameter to custom defined generators.
- * The reason why is not used KParameter is that you will want to provide
- * additional informations about parameter that is not available in KParameter class.
+ * Provides additional information about Class parameter to custom defined generators.
+ * The reason why KParameter is not used is that you will want to provide
+ * additional information about parameter that is not available in KParameter class.
  */
 data class ParameterInfo(
     val index: Int,
@@ -17,7 +17,7 @@ data class ParameterInfo(
 /**
  * Extension function that maps KParameter to ParameterInfo dataclass.
  */
-fun KParameter.toParameterInfo() = ParameterInfo(
+internal fun KParameter.toParameterInfo() = ParameterInfo(
     index = index,
     name = name.toString(),
     isOptional = isOptional,
diff --git a/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProvider.kt b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProvider.kt
index f215e05bd0..f62981d119 100644
--- a/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProvider.kt
+++ b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProvider.kt
@@ -2,7 +2,12 @@ package io.github.serpro69.kfaker.provider.misc
 
 import io.github.serpro69.kfaker.FakerConfig
 import io.github.serpro69.kfaker.RandomService
-import java.util.*
+import io.github.serpro69.kfaker.provider.misc.ConstructorFilterStrategy.MAX_NUM_OF_ARGS
+import io.github.serpro69.kfaker.provider.misc.ConstructorFilterStrategy.MIN_NUM_OF_ARGS
+import io.github.serpro69.kfaker.provider.misc.ConstructorFilterStrategy.NO_ARGS
+import io.github.serpro69.kfaker.provider.misc.FallbackStrategy.FAIL_IF_NOT_FOUND
+import io.github.serpro69.kfaker.provider.misc.FallbackStrategy.USE_MAX_NUM_OF_ARGS
+import io.github.serpro69.kfaker.provider.misc.FallbackStrategy.USE_MIN_NUM_OF_ARGS
 import kotlin.Boolean
 import kotlin.Char
 import kotlin.Double
@@ -14,6 +19,7 @@ import kotlin.String
 import kotlin.reflect.KClass
 import kotlin.reflect.KType
 import kotlin.reflect.KVisibility
+import kotlin.reflect.jvm.jvmName
 
 /**
  * Provider functionality for generating random class instances.
@@ -102,62 +108,67 @@ class RandomClassProvider {
     @JvmSynthetic
     @PublishedApi
     internal fun <T : Any> KClass<T>.randomClassInstance(config: RandomProviderConfig): T {
-        val defaultInstance: T? = if (
-            config.constructorParamSize == -1
-            && config.constructorFilterStrategy == ConstructorFilterStrategy.NO_ARGS
-        ) {
-            randomPrimitiveOrNull() as T? ?: try {
-                constructors.firstOrNull { it.parameters.isEmpty() && it.visibility == KVisibility.PUBLIC }?.call()
-            } catch (e: Exception) {
-                throw InstantiationException("Failed to instantiate $this")
-                    .initCause(e)
-            }
-        } else null
+        val defaultInstance: T? by lazy {
+            if (config.constructorParamSize == -1 && config.constructorFilterStrategy == NO_ARGS) {
+                randomPrimitiveOrNull() as T? ?: try {
+                    constructors.firstOrNull { it.parameters.isEmpty() && it.visibility == KVisibility.PUBLIC }?.call()
+                } catch (e: Exception) {
+                    throw InstantiationException("Failed to instantiate $this")
+                        .initCause(e)
+                }
+            } else null
+        }
 
-        return defaultInstance ?: objectInstance ?: run {
-            val constructors = constructors
-                .filter { it.visibility == KVisibility.PUBLIC }
+        val predefinedInstance: T? by lazy {
+            predefinedTypeOrNull(
+                config,
+                // it's not a constructor parameter, so set some hardcoded values for ParameterInfo
+                ParameterInfo(index = -1, name = jvmName, isOptional = false, isVararg = false)
+            ) as T?
+        }
+
+        return objectInstance ?: defaultInstance ?: run {
+            val constructors = constructors.filter { it.visibility == KVisibility.PUBLIC }
 
             val constructor = constructors.firstOrNull {
                 it.parameters.size == config.constructorParamSize
             } ?: when (config.constructorFilterStrategy) {
-                ConstructorFilterStrategy.MIN_NUM_OF_ARGS -> constructors.minByOrNull { it.parameters.size }
-                ConstructorFilterStrategy.MAX_NUM_OF_ARGS -> constructors.maxByOrNull { it.parameters.size }
+                MIN_NUM_OF_ARGS -> constructors.minByOrNull { it.parameters.size }
+                MAX_NUM_OF_ARGS -> constructors.maxByOrNull { it.parameters.size }
                 else -> {
                     when (config.fallbackStrategy) {
-                        FallbackStrategy.FAIL_IF_NOT_FOUND -> {
+                        FAIL_IF_NOT_FOUND -> {
                             throw NoSuchElementException("Constructor with 'parameters.size == ${config.constructorParamSize}' not found for $this")
                         }
-                        FallbackStrategy.USE_MIN_NUM_OF_ARGS -> constructors.minByOrNull { it.parameters.size }
-                        FallbackStrategy.USE_MAX_NUM_OF_ARGS -> constructors.maxByOrNull { it.parameters.size }
+                        USE_MIN_NUM_OF_ARGS -> constructors.minByOrNull { it.parameters.size }
+                        USE_MAX_NUM_OF_ARGS -> constructors.maxByOrNull { it.parameters.size }
                     }
                 }
-            } ?: throw NoSuchElementException("No suitable constructor found for $this")
-
-            val params = constructor.parameters
-                .map {
-
-                    val pInfo = it.toParameterInfo()
-
-                    val klass = it.type.classifier as KClass<*>
-                    when {
-                        config.namedParameterGenerators.containsKey(it.name) -> {
-                            config.namedParameterGenerators[it.name]?.invoke(pInfo)
-                        }
-                        it.type.isMarkedNullable && config.nullableGenerators.containsKey(klass) -> {
-                            config.nullableGenerators[klass]?.invoke(pInfo)
-                        }
-                        else -> {
-                            klass.predefinedTypeOrNull(config, pInfo)
-                                ?: klass.randomPrimitiveOrNull()
-                                ?: klass.objectInstance
-                                ?: klass.randomEnumOrNull()
-                                ?: klass.randomSealedClassOrNull(config)
-                                ?: klass.randomCollectionOrNull(it.type, config)
-                                ?: klass.randomClassInstance(config)
-                        }
+            }
+            ?: predefinedInstance?.let { return@run it }
+            ?: throw NoSuchElementException("No suitable constructor or predefined instance found for $this")
+
+            val params = constructor.parameters.map {
+                val pInfo = it.toParameterInfo()
+                val klass = it.type.classifier as KClass<*>
+                when {
+                    config.namedParameterGenerators.containsKey(it.name) -> {
+                        config.namedParameterGenerators[it.name]?.invoke(pInfo)
+                    }
+                    it.type.isMarkedNullable && config.nullableGenerators.containsKey(klass) -> {
+                        config.nullableGenerators[klass]?.invoke(pInfo)
+                    }
+                    else -> {
+                        klass.objectInstance
+                            ?: klass.predefinedTypeOrNull(config, pInfo)
+                            ?: klass.randomPrimitiveOrNull()
+                            ?: klass.randomEnumOrNull()
+                            ?: klass.randomSealedClassOrNull(config)
+                            ?: klass.randomCollectionOrNull(it.type, config)
+                            ?: klass.randomClassInstance(config)
                     }
                 }
+            }
 
             try {
                 constructor.call(*params.toTypedArray())
@@ -242,8 +253,8 @@ class RandomClassProvider {
 class RandomProviderConfig @PublishedApi internal constructor() {
     var collectionsSize: Int = 1
     var constructorParamSize: Int = -1
-    var constructorFilterStrategy: ConstructorFilterStrategy = ConstructorFilterStrategy.NO_ARGS
-    var fallbackStrategy: FallbackStrategy = FallbackStrategy.USE_MIN_NUM_OF_ARGS
+    var constructorFilterStrategy: ConstructorFilterStrategy = NO_ARGS
+    var fallbackStrategy: FallbackStrategy = USE_MIN_NUM_OF_ARGS
 
     @PublishedApi
     internal val namedParameterGenerators = mutableMapOf<String, (pInfo: ParameterInfo) -> Any?>()
@@ -277,14 +288,13 @@ class RandomProviderConfig @PublishedApi internal constructor() {
     inline fun <reified K : Any?> nullableTypeGenerator(noinline generator: (pInfo: ParameterInfo) -> K?) {
         nullableGenerators[K::class] = generator
     }
-
 }
 
 private fun RandomProviderConfig.reset() {
     collectionsSize = 1
     constructorParamSize = -1
-    constructorFilterStrategy = ConstructorFilterStrategy.NO_ARGS
-    fallbackStrategy = FallbackStrategy.USE_MIN_NUM_OF_ARGS
+    constructorFilterStrategy = NO_ARGS
+    fallbackStrategy = USE_MIN_NUM_OF_ARGS
     namedParameterGenerators.clear()
     predefinedGenerators.clear()
     nullableGenerators.clear()
diff --git a/docs/src/orchid/resources/wiki/extras.md b/docs/src/orchid/resources/wiki/extras.md
index 82f486b76d..264c50ba4b 100644
--- a/docs/src/orchid/resources/wiki/extras.md
+++ b/docs/src/orchid/resources/wiki/extras.md
@@ -9,7 +9,7 @@
 
 * [Random instance of any class](#random-instance-of-any-class)
   * [Random Class Instance Configuration](#random-class-instance-configuration)
-  * [Pre-configuring type generation for constructor arguments](#pre-configuring-type-generation-for-constructor-arguments)
+  * [Pre-configuring type generation](#pre-configuring-type-generation)
   * [Deterministic constructor selection](#deterministic-constructor-selection)
   * [Configuring the size of generated Collections](#configuring-the-size-of-generated-collections)
   * [Making a Copy or a New instance of RandomClassProvider](#making-a-new-instance-of-random-class-provider)
@@ -122,10 +122,11 @@ This configuration takes the most precedence and does not take into account conf
 
 <br>
 
-### Pre-Configuring type generation for constructor arguments
+### Pre-Configuring type generation
 
-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:
+#### 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:
 
 {% tabs %}
 
@@ -184,6 +185,63 @@ val person: Person = faker.randomProvider.randomClassInstance {
 
 {% endtabs %}
 
+<br>
+
+#### Pre-defined instance for classes with no public constructors
+
+By default, `randomClassInstance` can't generate classes with no public constructors, but this can be worked around by using `typeGenerator`:
+
+{% tabs %}
+
+{% kotlin "Kotlin" %}
+{% filter compileAs('md') %}
+```kotlin
+faker.randomProvider.randomClassInstance<Instant> {
+    typeGenerator<Instant> { Instant.now() }
+}
+```
+{% endfilter %}
+{% endkotlin %}
+
+{% endtabs %}
+
+You could also use the same approach to create interfaces, for example:
+
+{% tabs %}
+
+{% kotlin "Kotlin" %}
+{% filter compileAs('md') %}
+```kotlin
+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"
+```
+{% endfilter %}
+{% endkotlin %}
+
+{% endtabs %}
+
+A random class instance will be generated using the following precedence rules:
+
+- object instance
+- "default instance" of a class
+  - uses the public constructor with the least number of arguments, unless otherwise configured (see [Random Class Instance Configuration]({{ link(collectionType='wiki', collectionId='', itemId='Extras') }}#random-class-instance-configuration))
+- "predefined instance" of a class if no public constructors are found
+- failing all of the above, `NoSuchElementException` will be thrown
+
 {% btc %}{% endbtc %}
 
 <br>


Test patch:
diff --git a/core/src/test/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProviderTest.kt b/core/src/test/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProviderTest.kt
index cf742bb859..c264d625bf 100644
--- a/core/src/test/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProviderTest.kt
+++ b/core/src/test/kotlin/io/github/serpro69/kfaker/provider/misc/RandomClassProviderTest.kt
@@ -2,7 +2,6 @@ package io.github.serpro69.kfaker.provider.misc
 
 import io.github.serpro69.kfaker.FakerConfig
 import io.github.serpro69.kfaker.fakerConfig
-import io.kotest.assertions.assertSoftly
 import io.kotest.assertions.throwables.shouldNotThrow
 import io.kotest.assertions.throwables.shouldThrow
 import io.kotest.core.spec.style.DescribeSpec
@@ -12,7 +11,6 @@ import io.kotest.matchers.shouldBe
 import io.kotest.matchers.shouldNotBe
 import io.kotest.matchers.string.shouldHaveLength
 import io.kotest.matchers.types.instanceOf
-import io.kotest.matchers.types.shouldBeSameInstanceAs
 import io.kotest.matchers.types.shouldNotBeSameInstanceAs
 import java.util.*
 import kotlin.reflect.full.declaredMemberProperties
@@ -43,9 +41,24 @@ class RandomClassProviderTest : DescribeSpec({
         context("creating a random instance of the class") {
             val testClass: TestClass = randomProvider.randomClassInstance()
 
-            it("it should be instance of TestClass") {
+            it("should be instance of TestClass") {
                 testClass shouldBe instanceOf(TestClass::class)
             }
+            it("should follow precedence rules and try to generate a 'default instance' before using 'predefined instance'") {
+                class Bar(val id: Int)
+                class Baz(val bar: Bar)
+
+                val bar = randomProvider.randomClassInstance<Bar> {
+                    typeGenerator<Bar> { Bar(0) }
+                }
+
+                val baz = randomProvider.randomClassInstance<Baz> {
+                    typeGenerator<Baz> { Baz(Bar(1)) }
+                }
+
+                bar.id shouldNotBe 0
+                baz.bar.id shouldNotBe 1
+            }
         }
     }
 
@@ -122,16 +135,38 @@ class RandomClassProviderTest : DescribeSpec({
         }
     }
 
-    describe("a TestClass with private constructor") {
-        class TestClass private constructor()
-
-        context("creating a random instance of the class") {
+    describe("no public constructors") {
+        context("creating a random instance of a class") {
+            it("should return a predefined instance via typeGenerator") {
+                val testClassMin = randomProvider.randomClassInstance<TestClassNoPublic> {
+                    typeGenerator<TestClassNoPublic> { TestClassNoPublic.MIN }
+                }
+                val testClassMax = randomProvider.randomClassInstance<TestClassNoPublic> {
+                    typeGenerator<TestClassNoPublic> { TestClassNoPublic.MAX }
+                }
+                testClassMin.id shouldBe Int.MIN_VALUE
+                testClassMax.id shouldBe Int.MAX_VALUE
+            }
             it("exception is thrown") {
                 val exception = shouldThrow<NoSuchElementException> {
-                    randomProvider.randomClassInstance<TestClass>()
+                    randomProvider.randomClassInstance<TestClassNoPublic>()
+                }
+                exception.message shouldBe "No suitable constructor or predefined instance found for ${TestClassNoPublic::class}"
+            }
+        }
+        context("creating a random instance of an interface") {
+            it("should return a predefined interface instance via typeGenerator") {
+                val testInterface = randomProvider.randomClassInstance<TestInterface> {
+                    typeGenerator<TestInterface> {
+                        object : TestInterface {
+                            override val id: Int = 42
+                            override val name: String = "Deep Thought"
+                        }
+                    }
                 }
 
-                exception.message shouldBe "No suitable constructor found for ${TestClass::class}"
+                testInterface.id shouldBe 42
+                testInterface.name shouldBe "Deep Thought"
             }
         }
     }
@@ -178,20 +213,16 @@ class RandomClassProviderTest : DescribeSpec({
         context("creating a random instance of the class with custom generators") {
             val testClass: TestClass = randomProvider.randomClassInstance {
                 typeGenerator<UUID> { givenUuid }
-                @Suppress("RemoveExplicitTypeArguments")
                 typeGenerator<Int> { givenInt }
-                @Suppress("RemoveExplicitTypeArguments")
                 typeGenerator<String> { pInfo -> pInfo.toString() }
             }
 
             it("it should be a predefined UUID and primitives") {
-                assertSoftly {
-                    testClass shouldBe instanceOf(TestClass::class)
-                    testClass.id shouldBe givenUuid
-                    testClass.int shouldBe givenInt
-                    testClass.foo.int shouldBe givenInt
-                    testClass.bar shouldBe "ParameterInfo(index=0, name=bar, isOptional=false, isVararg=false)"
-                }
+                testClass shouldBe instanceOf(TestClass::class)
+                testClass.id shouldBe givenUuid
+                testClass.int shouldBe givenInt
+                testClass.foo.int shouldBe givenInt
+                testClass.bar shouldBe "ParameterInfo(index=0, name=bar, isOptional=false, isVararg=false)"
             }
         }
     }
@@ -219,14 +250,12 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("should use predefined UUIDs with parameter generators taking precedence over type generators") {
-                assertSoftly {
-                    testClass shouldBe instanceOf(TestClass::class)
-                    testClass.id shouldBe namedParameterGeneratedId
-                    testClass.id2 shouldBe typeGeneratedId
-                    testClass.nullableId shouldBe nullableNamedParameterGeneratedId
-                    testClass.nullableId2 shouldBe null
-                    testClass.foo shouldBe "ParameterInfo(index=4, name=foo, isOptional=true, isVararg=false)"
-                }
+                testClass shouldBe instanceOf(TestClass::class)
+                testClass.id shouldBe namedParameterGeneratedId
+                testClass.id2 shouldBe typeGeneratedId
+                testClass.nullableId shouldBe nullableNamedParameterGeneratedId
+                testClass.nullableId2 shouldBe null
+                testClass.foo shouldBe "ParameterInfo(index=4, name=foo, isOptional=true, isVararg=false)"
             }
         }
     }
@@ -248,24 +277,19 @@ class RandomClassProviderTest : DescribeSpec({
 
         context("creating a random instance of the class with nullable custom generators") {
             val testClass: TestClass = randomProvider.randomClassInstance {
-                @Suppress("RemoveExplicitTypeArguments")
                 typeGenerator<UUID> { givenUuid }
-                @Suppress("RemoveExplicitTypeArguments")
                 typeGenerator<Int> { givenInt } // use the not-null type generator and verify it is used for Int?
                 nullableTypeGenerator<Long?> { givenNullableLong }
-                @Suppress("RemoveExplicitTypeArguments")
                 typeGenerator<Long> { givenLong }
             }
 
             it("it should be a predefined UUID and primitives with nulls") {
-                assertSoftly {
-                    testClass shouldBe instanceOf(TestClass::class)
-                    testClass.id shouldBe givenUuid
-                    testClass.int shouldBe givenInt
-                    testClass.nullableLong shouldBe givenNullableLong
-                    testClass.notNullLong shouldBe givenLong
-                    testClass.foo.int shouldBe givenInt
-                }
+                testClass shouldBe instanceOf(TestClass::class)
+                testClass.id shouldBe givenUuid
+                testClass.int shouldBe givenInt
+                testClass.nullableLong shouldBe givenNullableLong
+                testClass.notNullLong shouldBe givenLong
+                testClass.foo.int shouldBe givenInt
             }
         }
     }
@@ -305,11 +329,9 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("constructor with specified args size is used") {
-                assertSoftly {
-                    testClass.foo shouldNotBe null
-                    testClass.bar shouldNotBe null
-                    testClass.baz shouldNotBe null
-                }
+                testClass.foo shouldNotBe null
+                testClass.bar shouldNotBe null
+                testClass.baz shouldNotBe null
             }
         }
 
@@ -319,11 +341,9 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("constructor with specified filter is used") {
-                assertSoftly {
-                    testClass.foo shouldNotBe null
-                    testClass.bar shouldNotBe null
-                    testClass.baz shouldNotBe null
-                }
+                testClass.foo shouldNotBe null
+                testClass.bar shouldNotBe null
+                testClass.baz shouldNotBe null
             }
         }
 
@@ -334,11 +354,9 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("constructor with specified fallback is used") {
-                assertSoftly {
-                    testClass.foo shouldNotBe null
-                    testClass.bar shouldNotBe null
-                    testClass.baz shouldNotBe null
-                }
+                testClass.foo shouldNotBe null
+                testClass.bar shouldNotBe null
+                testClass.baz shouldNotBe null
             }
         }
 
@@ -349,11 +367,9 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("constructor with specified fallback is used") {
-                assertSoftly {
-                    testClass.foo shouldNotBe null
-                    testClass.bar shouldNotBe null
-                    testClass.baz shouldNotBe null
-                }
+                testClass.foo shouldNotBe null
+                testClass.bar shouldNotBe null
+                testClass.baz shouldNotBe null
             }
         }
 
@@ -364,11 +380,9 @@ class RandomClassProviderTest : DescribeSpec({
             }
 
             it("constructor with specified fallback is used") {
-                assertSoftly {
-                    testClass.foo shouldNotBe null
-                    testClass.bar shouldNotBe null
-                    testClass.baz shouldBe null
-                }
+                testClass.foo shouldNotBe null
+                testClass.bar shouldNotBe null
+                testClass.baz shouldBe null
             }
         }
     }
@@ -385,32 +399,26 @@ class RandomClassProviderTest : DescribeSpec({
 
         it("should generate Collections with default size 1") {
             val testClass = randomProvider.randomClassInstance<TestClass>()
-            assertSoftly {
-                testClass.list shouldHaveSize 1
-                testClass.set shouldHaveSize 1
-                testClass.map shouldHaveSize 1
-            }
+            testClass.list shouldHaveSize 1
+            testClass.set shouldHaveSize 1
+            testClass.map shouldHaveSize 1
         }
 
         it("should generate Collections with pre-configured size") {
             val testClass = randomProvider.randomClassInstance<TestClass> {
                 collectionsSize = 10
             }
-            assertSoftly {
-                testClass.list shouldHaveSize 10
-                testClass.set shouldHaveSize 10
-                testClass.map shouldHaveSize 10
-            }
+            testClass.list shouldHaveSize 10
+            testClass.set shouldHaveSize 10
+            testClass.map shouldHaveSize 10
         }
         it("should generate Collections with pre-configured type generation") {
             val testClass = randomProvider.randomClassInstance<TestClass> {
                 typeGenerator<List<Foo>> { listOf() }
             }
-            assertSoftly {
-                testClass.list shouldHaveSize 0
-                testClass.set shouldHaveSize 1
-                testClass.map shouldHaveSize 1
-            }
+            testClass.list shouldHaveSize 0
+            testClass.set shouldHaveSize 1
+            testClass.map shouldHaveSize 1
         }
     }
 
@@ -441,16 +449,14 @@ class RandomClassProviderTest : DescribeSpec({
 
     describe("a primitive type") {
         it("should not throw an exception when no suitable constructor exists") {
-            assertSoftly {
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Double>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Float>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Long>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Int>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Short>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Byte>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Boolean>() }
-                shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Char>() }
-            }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Double>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Float>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Long>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Int>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Short>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Byte>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Boolean>() }
+            shouldNotThrow<NoSuchElementException> { randomProvider.randomClassInstance<Char>() }
         }
 
         it("should generate a random string") {
@@ -589,21 +595,21 @@ class RandomClassProviderTest : DescribeSpec({
                     randomClassInstance<Baz>().nullable shouldBe null
                 }
             }
-/*            it("should reset configuration to defaults on fakerConfig level") {
-                val c = cfg()
-                with(RandomClassProvider(c)) {
-                    configure {
-                        typeGenerator { Foo(12177) }
-                        nullableTypeGenerator<String> { null }
-                    }
-                    c.randomProviderConfig?.reset()
-                    randomClassInstance<Bar>().foo.int shouldNotBe 12177
-                    randomClassInstance<Baz>().foo.int shouldNotBe 12177
-                    randomClassInstance<Baz>().bar.foo.int shouldNotBe 12177
-                    randomClassInstance<Baz>().test shouldNotBe 1
-                    randomClassInstance<Baz>().nullable shouldNotBe null
-                }
-            }*/
+            /*            it("should reset configuration to defaults on fakerConfig level") {
+                            val c = cfg()
+                            with(RandomClassProvider(c)) {
+                                configure {
+                                    typeGenerator { Foo(12177) }
+                                    nullableTypeGenerator<String> { null }
+                                }
+                                c.randomProviderConfig?.reset()
+                                randomClassInstance<Bar>().foo.int shouldNotBe 12177
+                                randomClassInstance<Baz>().foo.int shouldNotBe 12177
+                                randomClassInstance<Baz>().bar.foo.int shouldNotBe 12177
+                                randomClassInstance<Baz>().test shouldNotBe 1
+                                randomClassInstance<Baz>().nullable shouldNotBe null
+                            }
+                        }*/
         }
     }
 
@@ -726,3 +732,15 @@ sealed class TestSealedCls {
 class Go(val name: String) : TestSealedCls()
 
 object TestObject
+
+class TestClassNoPublic private constructor(val id: Int) {
+    companion object {
+        val MIN = TestClassNoPublic(Int.MIN_VALUE)
+        val MAX = TestClassNoPublic(Int.MAX_VALUE)
+    }
+}
+
+interface TestInterface {
+    val id: Int
+    val name: String
+}