Hugh
Hugh

Reputation: 1163

How to stub varargs in Mockito to match only one argument

I'd like to stub some code so that a vararg method returns true when one of the arguments matches a specific value. For example, given the existing code that I can't change:

(Using Kotlin here, but I figure this applies to any Java situation.)

class Foo {
    fun bar(vararg strings : String) : Boolean {
        // Iterates `strings` and returns true when one satisfies some criteria
    }
}

... I want to write stub code similar to this:

val foo = Foo()
whenever(foo.bar(eq("AAA"))).thenReturn(true)

This works fine when the call is exactly foo.bar("AAA").

However, there are times when the code under test makes the call foo.bar("AAA", "BBB"), and in those cases, it fails.

How can I revise my stub code so it works when any number of varargs are passed in the call?

Edit Flagged as a possible duplicate; in that case, the scenario contemplates the complete omission of the varargs in the call. Here, I'm trying to match one specific element of the varargs array.

Upvotes: 3

Views: 841

Answers (2)

Lino
Lino

Reputation: 19926

You have to stub your method 2 times. First the least specific stub:

val foo = Foo()
whenever(foo.bar(any())).thenReturn(false) // or whatever you want to return/throw here

And then the more specific single argument method:

whenever(foo.bar(eq("AAA"))).thenReturn(true)

After your comment you may aswell use something like this (using Java this time):

when(foo.bar(any())).thenAnswer(invocation -> {
    for (Object argument : invocation.getArguments()) {
        if ("AAA".equals(argument)) return true;
    }
    return false;
});

And the same in Kotlin

whenever(foo.bar(any()).thenAnswer {
    it.arguments.contains("AAA")
}

Upvotes: 4

ToYonos
ToYonos

Reputation: 16833

You can create your own matcher :

public class MyVarargMatcher extends ArgumentMatcher<String[]> implements VarargMatcher
{
    private String expectedFirstValue;

    public MyVarargMatcher(String expectedFirstValue)
    {
        this.expectedFirstValue = expectedFirstValue;
    }

    @Override
    public boolean matches(Object varargArgument) {
        if (varargArgument instanceof String[])
        {
            String[] args = (String[]) varargArgument;
            return Stream.of(args).anyMatch(expectedFirstValue::equals);
        }
        return false;
    }
}

And then use it like that (Java code) :

Foo foo = Mockito.mock(Foo.class);
Mockito.doReturn(true).when(foo).bar(Mockito.argThat(new MyVarargMatcher("AAA")));

edited with the op's comment : As long as "AAA" is one of the args, it should return true

Upvotes: 2

Related Questions