How to properly match varargs in Mockito
Asked Answered
C

13

197

I've been trying to get to mock a method with vararg parameters using Mockito:

interface A {
  B b(int x, int y, C... c);
}

A a = mock(A.class);
B b = mock(B.class);

when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
assertEquals(b, a.b(1, 2));

This doesn't work, however if I do this instead:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));

This works, despite that I have completely omitted the varargs argument when stubbing the method.

Any clues?

Cerulean answered 13/4, 2010 at 17:8 Comment(1)
the fact that last example works is rather trivial since it matches the case when zero varargs parameters passed.Hallucinatory
H
280

Mockito 1.8.1 introduced anyVararg() matcher:

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

Also see history for this: https://code.google.com/archive/p/mockito/issues/62

Edit new syntax after deprecation:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
Hallucinatory answered 14/4, 2010 at 2:53 Comment(7)
anyVararg() has Object as its return type. To make it compatible with any var arg types (e.g. String ..., Integer ..., etc.), do an explicit casting. For example, if you have doSomething(Integer number, String ... args) you can do the mock/stub code with something like when(mock).doSomething(eq(1), (String) anyVarargs()). That should take care of the compilation error.Kauai
for info anyVararg is now deprecated: "@deprecated as of 2.1.0 use any()"Characharabanc
Matchers is now deprecated in order to avoid a name clash with org.hamcrest.Matchers class and will likely be removed in mockito v3.0. Use ArgumentMatchers instead.Congenital
anyVararg() has been deprecated in ArgumentMatchers as well.Undeviating
@Undeviating thanks! So what's the replacement/alternative?Moreau
N.B. If you have an ambiguous method call problem (e.g. there is one method that takes a single argument and another method that takes varargs), you can fix the problem by casting the any method to a typed array. For instance: (String[]) any().Resnatron
ArgumentMatchers.<String>any() did not work for me on Mockito 3.3 but (String[]) any() did.Kamila
P
33

A somewhat undocumented feature: If you want to develop a custom Matcher that matches vararg arguments you need to have it implement org.mockito.internal.matchers.VarargMatcher for it to work correctly. It's an empty marker interface, without which Mockito will not correctly compare arguments when invoking a method with varargs using your Matcher.

For example:

class MyVarargMatcher extends ArgumentMatcher<C[]> implements VarargMatcher {
    @Override public boolean matches(Object varargArgument) {
        return /* does it match? */ true;
    }
}

when(a.b(anyInt(), anyInt(), argThat(new MyVarargMatcher()))).thenReturn(b);
Preshrunk answered 3/11, 2011 at 0:15 Comment(0)
E
9

Building on Eli Levine's answer here is a more generic solution:

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;

import static org.mockito.Matchers.argThat;

public class VarArgMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher {

    public static <T> T[] varArgThat(Matcher<T[]> hamcrestMatcher) {
        argThat(new VarArgMatcher(hamcrestMatcher));
        return null;
    }

    private final Matcher<T[]> hamcrestMatcher;

    private VarArgMatcher(Matcher<T[]> hamcrestMatcher) {
        this.hamcrestMatcher = hamcrestMatcher;
    }

    @Override
    public boolean matches(Object o) {
        return hamcrestMatcher.matches(o);
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("has varargs: ").appendDescriptionOf(hamcrestMatcher);
    }

}

Then you can use it with hamcrest's array matchers thus:

verify(a).b(VarArgMatcher.varArgThat(
            org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));

(Obviously static imports will render this more readable.)

Eggett answered 30/6, 2015 at 15:40 Comment(4)
Nice. This should be built into Mockito IMO.Colton
I filed an issue against Hamcrest to add something like this. See github.com/mockito/mockito/issues/356Rojo
Is this for Mockito 1? I get various compilation errors when trying to compile against 2.10.Kelseykelsi
@Kelseykelsi it looks like the 2 release was still in beta when I wrote this answer, so yes it was probably written for Mockito v1.10.19 or thereabouts. (github.com/mockito/mockito/releases) It is probably updatable... :-DEggett
R
8

I have been using the code in Peter Westmacott's answer however with Mockito 2.2.15 you can now do the following:

verify(a).method(100L, arg1, arg2, arg3)

where arg1, arg2, arg3 are varargs.

Rojo answered 14/11, 2016 at 9:26 Comment(0)
F
6

With Mockito 5 any() does not match varargs in our project. In Mockito 4 this has worked. In Mockito 5 now only any(<VAR-ARG-TYPE>[].class) does match varargs!

Translated to your example this works in Mockito 5:

when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
Fringe answered 6/9, 2023 at 14:18 Comment(0)
C
3

Building on topchef's answer,

For 2.0.31-beta I had to use Mockito.anyVararg instead of Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
Connective answered 29/4, 2016 at 17:44 Comment(1)
for info anyVararg is now deprecated: "@deprecated as of 2.1.0 use any()"Characharabanc
G
3

I had to use the any(Class type) method to match an array arg being passed as a varargs parameter.

ArgumentMatchers.any(Class type)

Code in the implementation is vararg aware.

reportMatcher(new InstanceOf.VarArgAware(

In my case where matching a String[] arg to a String... param the following worked:-

any(String.class)
Grillo answered 24/8, 2021 at 12:19 Comment(0)
A
1

Adapting the answer from @topchef,

Mockito.when(a.b(Mockito.anyInt(), Mockito.anyInt(), Mockito.any())).thenReturn(b);

Per the java docs for Mockito 2.23.4, Mockito.any() "Matches anything, including nulls and varargs."

Ambros answered 25/1, 2019 at 14:42 Comment(0)
C
1

You can accomplish this by passing an ArgumentCaptor capture and then retrieving the varargs as a list using "getAllValues", see: https://mcmap.net/q/129955/-how-match-varargs-in-mockito-2

Cobbler answered 10/4, 2019 at 21:47 Comment(1)
(mockito-core = ver 4.5.1) It does work by "getAllValues()", although it is very weird. At compile time, the compiler thought "getAllValues()" is a "List<Integer[]>". But at runtime, it is a "List<Integer>" instead.Isomerous
K
1

As the other answers make sense and make tests work obviously, I still recommend to test as if the method didn't take a vararg, but rather regular well-defined parameters instead. This helps in situations where overridden methods in connection with possible ambiguous parameters are in place, like an SLF4J-logger:

to test:

jobLogger.info("{} finished: {} tasks processed with {} failures, took {}", jobName, count, errors, duration);

This has a bunch of overrides and the important method being declared like so

Logger.info(String, Object...)

verification:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyInt(), anyString());

proof that the above works as errors is an integer and not a long, so the following wouldn't run:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyLong(), anyString());

So you can easily use when() instead of the verify()-stuff to set up the required return value.

And it probably shows more of the intent and is more readable. Captures can also be used here and they are much easier accessible this way.

Tested with Mockito 2.15

Krugersdorp answered 14/6, 2021 at 7:22 Comment(0)
L
0

In my case the signature of the method that I want to capture its argument is:

public byte[] write(byte ... data) throws IOException;

In this case you should cast to byte array explicitly:

when(spi.write((byte[])anyVararg())).thenReturn(someValue);

I'm using mockito version 1.10.19

Lo answered 3/5, 2016 at 18:31 Comment(0)
M
0

You can also loop over the arguments:

Object[] args = invocation.getArguments(); 
for( int argNo = 0; argNo < args.length; ++argNo) { 
    // ... do something with args[argNo] 
}

for example check their types and cast them appropriately, add to a list or whatever.

Middlings answered 10/2, 2017 at 12:20 Comment(0)
E
0

I've looked up for various ways but most of the ways are not working as described. What worked for me is.

Method Signature:

ReturnType someMethod(String... strings)

Mockito to handle above method:

when(someMethod((String[])Mockito.any())).thenReturn(dummyReturnTypeObject);

mocking a method with varargs

if your mock is successful then the value that appears for the mocked step wouldn't be null

result of mocking a method with varargs

Enchantress answered 20/7, 2023 at 13:27 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Worse

© 2022 - 2024 — McMap. All rights reserved.