Skip to content

Commit 3979104

Browse files
authored
Fix capturing varargs with Mockito 5 (#490)
Fixes #474
1 parent d92b616 commit 3979104

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

mockito-kotlin/src/main/kotlin/org/mockito/kotlin/ArgumentCaptor.kt

+15-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ package org.mockito.kotlin
2727

2828
import org.mockito.kotlin.internal.createInstance
2929
import org.mockito.ArgumentCaptor
30+
import java.lang.reflect.Array
3031
import kotlin.reflect.KClass
3132

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

196197
@Suppress("UNCHECKED_CAST")
197198
fun capture(): T {
198-
return captor.capture() ?: createInstance(tClass) as T
199+
// Special handling for arrays to make it work for varargs
200+
// In Kotlin we want have to capture vararg like this `verify(m).methodName(*captor.capture())` to make the types work
201+
// If we return null for array types, the spread `*` operator will fail with NPE
202+
// If we return empty array, it will fail in MatchersBinder.validateMatchers
203+
// In Java, `captor.capture` returns null and so the method is called with `[null]`
204+
// In Kotlin, we have to create `[null]` explicitly.
205+
// This code-path is applied for non-vararg array arguments as well, but it seems to work fine.
206+
return captor.capture() ?: if (tClass.java.isArray) {
207+
singleElementArray()
208+
} else {
209+
createInstance(tClass)
210+
} as T
199211
}
212+
213+
private fun singleElementArray(): Any? = Array.newInstance(tClass.java.componentType, 1)
200214
}
201215

202216
val <T> ArgumentCaptor<T>.firstValue: T

tests/src/test/kotlin/test/ArgumentCaptorTest.kt

+84
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,88 @@ class ArgumentCaptorTest : TestBase() {
226226
}
227227
}
228228
}
229+
230+
@Test
231+
fun argumentCaptor_vararg() {
232+
/* Given */
233+
val m: Methods = mock()
234+
235+
/* When */
236+
m.varargBooleanResult("a", "b", "c")
237+
238+
/* Then */
239+
val captor = argumentCaptor<Array<String>>()
240+
verify(m).varargBooleanResult(*captor.capture())
241+
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
242+
}
243+
244+
@Test
245+
fun argumentCaptor_empty_vararg() {
246+
/* Given */
247+
val m: Methods = mock()
248+
249+
/* When */
250+
m.varargBooleanResult()
251+
252+
/* Then */
253+
val captor = argumentCaptor<Array<String>>()
254+
verify(m).varargBooleanResult(*captor.capture())
255+
expect(captor.firstValue.toList()).toBe(listOf())
256+
}
257+
258+
@Test
259+
fun argumentCaptor_arg_vararg() {
260+
/* Given */
261+
val m: Methods = mock()
262+
263+
/* When */
264+
m.argAndVararg("first", "a", "b", "c")
265+
266+
/* Then */
267+
val captor = argumentCaptor<Array<String>>()
268+
verify(m).argAndVararg(any(), *captor.capture())
269+
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
270+
}
271+
272+
@Test
273+
fun argumentCaptor_intarray() {
274+
/* Given */
275+
val m: Methods = mock()
276+
277+
/* When */
278+
m.intArray(intArrayOf(1, 2, 3))
279+
280+
/* Then */
281+
val captor = argumentCaptor<IntArray>()
282+
verify(m).intArray(captor.capture())
283+
expect(captor.firstValue.toList()).toBe(listOf(1, 2, 3))
284+
}
285+
286+
@Test
287+
fun argumentCaptor_array() {
288+
/* Given */
289+
val m: Methods = mock()
290+
291+
/* When */
292+
m.stringArray(arrayOf("a", "b", "c"))
293+
294+
/* Then */
295+
val captor = argumentCaptor<Array<String>>()
296+
verify(m).stringArray(captor.capture())
297+
expect(captor.firstValue.toList()).toBe(listOf("a", "b", "c"))
298+
}
299+
300+
@Test
301+
fun argumentCaptor_empty_array() {
302+
/* Given */
303+
val m: Methods = mock()
304+
305+
/* When */
306+
m.stringArray(arrayOf())
307+
308+
/* Then */
309+
val captor = argumentCaptor<Array<String>>()
310+
verify(m).stringArray(captor.capture())
311+
expect(captor.firstValue.toList()).toBe(listOf())
312+
}
229313
}

tests/src/test/kotlin/test/Classes.kt

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ interface Methods {
7070
fun nullableStringResult(): String?
7171
fun builderMethod(): Methods
7272
fun varargBooleanResult(vararg values: String): Boolean
73+
fun stringArray(a: Array<String>)
74+
fun argAndVararg(s: String, vararg a: String)
7375

7476
fun nonDefaultReturnType(): ExtraInterface
7577
}

0 commit comments

Comments
 (0)