diff --git a/mockito-kotlin/src/main/kotlin/com/nhaarman/mockito_kotlin/CreateInstance.kt b/mockito-kotlin/src/main/kotlin/com/nhaarman/mockito_kotlin/CreateInstance.kt index f38ea903..cab7694e 100644 --- a/mockito-kotlin/src/main/kotlin/com/nhaarman/mockito_kotlin/CreateInstance.kt +++ b/mockito-kotlin/src/main/kotlin/com/nhaarman/mockito_kotlin/CreateInstance.kt @@ -34,10 +34,7 @@ import java.lang.reflect.InvocationTargetException import java.lang.reflect.Modifier import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.KType -import kotlin.reflect.defaultType +import kotlin.reflect.* import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.jvmName @@ -84,12 +81,12 @@ fun createInstance(kClass: KClass): T { */ private fun KClass.easiestConstructor(): KFunction { return constructors - .sortedBy { it.parameters.size } + .sortedBy { it.parameters.withoutOptionalParameters().size } .withoutParametersOfType(this.defaultType) .withoutArrayParameters() - .firstOrNull() ?: constructors.sortedBy { it.parameters.size } - .withoutParametersOfType(this.defaultType) - .first() + .firstOrNull() ?: constructors.sortedBy { it.parameters.withoutOptionalParameters().size } + .withoutParametersOfType(this.defaultType) + .first() } private fun List>.withoutArrayParameters() = filter { @@ -101,10 +98,12 @@ private fun List>.withoutArrayParameters() = filter { * This is especially useful to avoid infinite loops where constructors * accepting a parameter of their own type, e.g. 'copy constructors'. */ -private fun List>.withoutParametersOfType(type: KType) = filter { +private fun List>.withoutParametersOfType(type: KType) = filter { it.parameters.filter { it.type == type }.isEmpty() } +private fun List.withoutOptionalParameters() = filterNot { it.isOptional } + @Suppress("SENSELESS_COMPARISON") private fun KClass<*>.hasObjectInstance() = objectInstance != null @@ -166,7 +165,7 @@ private fun KClass.toClassObject(): T { private fun KFunction.newInstance(): T { try { isAccessible = true - return callBy(parameters.associate { + return callBy(parameters.withoutOptionalParameters().associate { it to it.type.createNullableInstance() }) } catch(e: InvocationTargetException) { diff --git a/mockito-kotlin/src/test/kotlin/CreateInstanceTest.kt b/mockito-kotlin/src/test/kotlin/CreateInstanceTest.kt index a643a506..991bf735 100644 --- a/mockito-kotlin/src/test/kotlin/CreateInstanceTest.kt +++ b/mockito-kotlin/src/test/kotlin/CreateInstanceTest.kt @@ -457,6 +457,25 @@ class CreateInstanceTest { expect(result).toNotBeNull() } + @Test + fun optionalParametersAreSkippedWhenSorting() { + /* When */ + val result = createInstance(WithDefaultParameters::class) + + /* Then */ + expect(result).toNotBeNull() + } + + @Test + fun defaultValuesAreUsedWithOptionalParameters() { + /* When */ + val result = createInstance(WithDefaultParameters::class) + + /* Then */ + expect(result.first).toBe(1) + expect(result.second).toBe(2) + } + private class PrivateClass private constructor(val data: String) class ClosedClass @@ -503,7 +522,18 @@ class CreateInstanceTest { */ data class WithCopyConstructor private constructor(val x: String, val y: String) { - constructor(other: WithCopyConstructor): this(other.x, other.y) + constructor(other: WithCopyConstructor) : this(other.x, other.y) + } + + /** + * A class that uses default parameters, but with a constructor without parameters that fails. + * This is to make sure default parameters are not counted when sorting by parameter size. + */ + class WithDefaultParameters constructor(val first: Int = 1, val second: Int = 2) { + + constructor(first: Int) : this() { + error("Should not be called") + } } enum class MyEnum { VALUE, ANOTHER_VALUE }