Skip to content

Commit 42cee20

Browse files
committed
Don't count optional parameters when trying to find an easy constructor
1 parent e43ccea commit 42cee20

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

mockito-kotlin/src/main/kotlin/com/nhaarman/mockito_kotlin/CreateInstance.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ import java.lang.reflect.InvocationTargetException
3434
import java.lang.reflect.Modifier
3535
import java.lang.reflect.ParameterizedType
3636
import java.lang.reflect.Type
37-
import kotlin.reflect.KClass
38-
import kotlin.reflect.KFunction
39-
import kotlin.reflect.KType
40-
import kotlin.reflect.defaultType
37+
import kotlin.reflect.*
4138
import kotlin.reflect.jvm.isAccessible
4239
import kotlin.reflect.jvm.javaType
4340
import kotlin.reflect.jvm.jvmName
@@ -84,12 +81,12 @@ fun <T : Any> createInstance(kClass: KClass<T>): T {
8481
*/
8582
private fun <T : Any> KClass<T>.easiestConstructor(): KFunction<T> {
8683
return constructors
87-
.sortedBy { it.parameters.size }
84+
.sortedBy { it.parameters.withoutOptionalParameters().size }
8885
.withoutParametersOfType(this.defaultType)
8986
.withoutArrayParameters()
90-
.firstOrNull() ?: constructors.sortedBy { it.parameters.size }
91-
.withoutParametersOfType(this.defaultType)
92-
.first()
87+
.firstOrNull() ?: constructors.sortedBy { it.parameters.withoutOptionalParameters().size }
88+
.withoutParametersOfType(this.defaultType)
89+
.first()
9390
}
9491

9592
private fun <T> List<KFunction<T>>.withoutArrayParameters() = filter {
@@ -101,10 +98,12 @@ private fun <T> List<KFunction<T>>.withoutArrayParameters() = filter {
10198
* This is especially useful to avoid infinite loops where constructors
10299
* accepting a parameter of their own type, e.g. 'copy constructors'.
103100
*/
104-
private fun <T: Any> List<KFunction<T>>.withoutParametersOfType(type: KType) = filter {
101+
private fun <T : Any> List<KFunction<T>>.withoutParametersOfType(type: KType) = filter {
105102
it.parameters.filter { it.type == type }.isEmpty()
106103
}
107104

105+
private fun List<KParameter>.withoutOptionalParameters() = filterNot { it.isOptional }
106+
108107
@Suppress("SENSELESS_COMPARISON")
109108
private fun KClass<*>.hasObjectInstance() = objectInstance != null
110109

@@ -166,7 +165,7 @@ private fun <T : Any> KClass<T>.toClassObject(): T {
166165
private fun <T : Any> KFunction<T>.newInstance(): T {
167166
try {
168167
isAccessible = true
169-
return callBy(parameters.associate {
168+
return callBy(parameters.withoutOptionalParameters().associate {
170169
it to it.type.createNullableInstance<T>()
171170
})
172171
} catch(e: InvocationTargetException) {

mockito-kotlin/src/test/kotlin/CreateInstanceTest.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,25 @@ class CreateInstanceTest {
457457
expect(result).toNotBeNull()
458458
}
459459

460+
@Test
461+
fun optionalParametersAreSkippedWhenSorting() {
462+
/* When */
463+
val result = createInstance(WithDefaultParameters::class)
464+
465+
/* Then */
466+
expect(result).toNotBeNull()
467+
}
468+
469+
@Test
470+
fun defaultValuesAreUsedWithOptionalParameters() {
471+
/* When */
472+
val result = createInstance(WithDefaultParameters::class)
473+
474+
/* Then */
475+
expect(result.first).toBe(1)
476+
expect(result.second).toBe(2)
477+
}
478+
460479
private class PrivateClass private constructor(val data: String)
461480

462481
class ClosedClass
@@ -503,7 +522,18 @@ class CreateInstanceTest {
503522
*/
504523
data class WithCopyConstructor private constructor(val x: String,
505524
val y: String) {
506-
constructor(other: WithCopyConstructor): this(other.x, other.y)
525+
constructor(other: WithCopyConstructor) : this(other.x, other.y)
526+
}
527+
528+
/**
529+
* A class that uses default parameters, but with a constructor without parameters that fails.
530+
* This is to make sure default parameters are not counted when sorting by parameter size.
531+
*/
532+
class WithDefaultParameters constructor(val first: Int = 1, val second: Int = 2) {
533+
534+
constructor(first: Int) : this() {
535+
error("Should not be called")
536+
}
507537
}
508538

509539
enum class MyEnum { VALUE, ANOTHER_VALUE }

0 commit comments

Comments
 (0)