How to properly match varargs in Mockito

JavaMockingVariadic FunctionsMockito

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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);

Solution 2 - Java

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);

Solution 3 - Java

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.)

Solution 4 - Java

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.

Solution 5 - Java

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);

Solution 6 - Java

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)

Solution 7 - Java

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

Solution 8 - Java

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.

Solution 9 - Java

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."

Solution 10 - Java

You can accomplish this by passing an ArgumentCaptor capture and then retrieving the varargs as a list using "getAllValues", see: https://stackoverflow.com/a/55621731/11342928

Solution 11 - Java

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionqualidafialView Question on Stackoverflow
Solution 1 - JavatopchefView Answer on Stackoverflow
Solution 2 - JavaEli LevineView Answer on Stackoverflow
Solution 3 - JavaRachel K. WestmacottView Answer on Stackoverflow
Solution 4 - JavaMarkView Answer on Stackoverflow
Solution 5 - JavaNPikeView Answer on Stackoverflow
Solution 6 - JavamattocarambaView Answer on Stackoverflow
Solution 7 - JavaartronicsView Answer on Stackoverflow
Solution 8 - JavaRichard WhiteheadView Answer on Stackoverflow
Solution 9 - JavaCraigView Answer on Stackoverflow
Solution 10 - JavaClarke LacherView Answer on Stackoverflow
Solution 11 - JavasjngmView Answer on Stackoverflow