Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#474 Fix vararg argument matcher #490

Merged
merged 2 commits into from Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -27,6 +27,7 @@ package org.mockito.kotlin

import org.mockito.kotlin.internal.createInstance
import org.mockito.ArgumentCaptor
import java.lang.reflect.Array
import kotlin.reflect.KClass

/**
Expand Down Expand Up @@ -195,8 +196,21 @@ class KArgumentCaptor<out T : Any?>(

@Suppress("UNCHECKED_CAST")
fun capture(): T {
return captor.capture() ?: createInstance(tClass) as T
// Special handling for arrays to make it work for varargs
// In Kotlin we want have to capture vararg like this `verify(m).methodName(*captor.capture())` to make the types work
// If we return null for array types, the spread `*` operator will fail with NPE
// If we return empty array, it will fail in MatchersBinder.validateMatchers
// In Java, `captor.capture` returns null and so the method is called with `[null]`
// In Kotlin, we have to create `[null]` explicitly.
// This code-path is applied for non-vararg array arguments as well, but it seems to work fine.
return captor.capture() ?: if (tClass.java.isArray) {
singleElementArray()
} else {
createInstance(tClass)
} as T
}

private fun singleElementArray(): Any? = Array.newInstance(tClass.java.componentType, 1)
}

val <T> ArgumentCaptor<T>.firstValue: T
Expand Down
84 changes: 84 additions & 0 deletions tests/src/test/kotlin/test/ArgumentCaptorTest.kt
Expand Up @@ -226,4 +226,88 @@ class ArgumentCaptorTest : TestBase() {
}
}
}

@Test
fun argumentCaptor_vararg() {
/* Given */
val m: Methods = mock()

/* When */
m.varargBooleanResult("a", "b", "c")

/* Then */
val captor = argumentCaptor<Array<String>>()
verify(m).varargBooleanResult(*captor.capture())
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
}

@Test
fun argumentCaptor_empty_vararg() {
/* Given */
val m: Methods = mock()

/* When */
m.varargBooleanResult()

/* Then */
val captor = argumentCaptor<Array<String>>()
verify(m).varargBooleanResult(*captor.capture())
expect(captor.firstValue.toList()).toBe(listOf())
}

@Test
fun argumentCaptor_arg_vararg() {
/* Given */
val m: Methods = mock()

/* When */
m.argAndVararg("first", "a", "b", "c")

/* Then */
val captor = argumentCaptor<Array<String>>()
verify(m).argAndVararg(any(), *captor.capture())
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
}

@Test
fun argumentCaptor_intarray() {
/* Given */
val m: Methods = mock()

/* When */
m.intArray(intArrayOf(1, 2, 3))

/* Then */
val captor = argumentCaptor<IntArray>()
verify(m).intArray(captor.capture())
expect(captor.firstValue.toList()).toBe(listOf(1, 2, 3))
}

@Test
fun argumentCaptor_array() {
/* Given */
val m: Methods = mock()

/* When */
m.stringArray(arrayOf("a", "b", "c"))

/* Then */
val captor = argumentCaptor<Array<String>>()
verify(m).stringArray(captor.capture())
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
}

@Test
fun argumentCaptor_empty_array() {
/* Given */
val m: Methods = mock()

/* When */
m.stringArray(arrayOf())

/* Then */
val captor = argumentCaptor<Array<String>>()
verify(m).stringArray(captor.capture())
expect(captor.firstValue.toList()).toBe(listOf())
}
}
2 changes: 2 additions & 0 deletions tests/src/test/kotlin/test/Classes.kt
Expand Up @@ -70,6 +70,8 @@ interface Methods {
fun nullableStringResult(): String?
fun builderMethod(): Methods
fun varargBooleanResult(vararg values: String): Boolean
fun stringArray(a: Array<String>)
fun argAndVararg(s: String, vararg a: String)

fun nonDefaultReturnType(): ExtraInterface
}
Expand Down