Jak poprawnie dopasować varargy w Mockito

152

Próbowałem uzyskać mock metody z parametrami vararg przy użyciu 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));

To nie działa, jeśli jednak zrobię to:

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

Działa to pomimo tego, że całkowicie pominąłem argument varargs podczas krojenia metody.

Jakieś wskazówki?

Qualidafial
źródło
fakt, że ostatni przykład działa, jest raczej trywialny, ponieważ pasuje do przypadku, gdy przekazano zero parametrów varargs.
topchef

Odpowiedzi:

235

Mockito 1.8.1 wprowadziło dopasowanie dopasowujące anyVararg () :

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

Zobacz także historię: https://code.google.com/archive/p/mockito/issues/62

Edytuj nową składnię po wycofaniu:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
topchef
źródło
26
anyVararg()ma Object jako typ zwracany. Aby był zgodny z dowolnymi typami argumentów var (np. String ..., Integer ... itp.), Wykonaj jawne rzutowanie. Na przykład, jeśli masz doSomething(Integer number, String ... args), możesz wykonać próbny kod / skrót za pomocą czegoś takiego jak when(mock).doSomething(eq(1), (String) anyVarargs()). To powinno zająć się błędem kompilacji.
Psycho Punch
15
dla informacji anyVararg jest teraz przestarzałe: „@deprecated od 2.1.0 użyj any ()”
alexbt
5
Matchersjest teraz przestarzałe, aby uniknąć org.hamcrest.Matcherskolizji nazw z klasą i prawdopodobnie zostanie usunięte w mockito v3.0. Użyj ArgumentMatcherszamiast tego.
JonyD,
31

Nieco nieudokumentowana funkcja: jeśli chcesz opracować niestandardowy Matcher, który pasuje do argumentów vararg, musisz mieć go zaimplementowany org.mockito.internal.matchers.VarargMatcher, aby działał poprawnie. Jest to interfejs z pustymi znacznikami, bez którego Mockito nie będzie poprawnie porównywał argumentów podczas wywoływania metody z varargami przy użyciu Twojego Matchera.

Na przykład:

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);
Eli Levine
źródło
7

Opierając się na odpowiedzi Eli Levine'a, jest to bardziej ogólne rozwiązanie:

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

}

Następnie możesz go użyć z dopasowującymi tablicami hamcrest w ten sposób:

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

(Oczywiście import statyczny sprawi, że będzie to bardziej czytelne).

Peter Westmacott
źródło
Miły. Powinno to być wbudowane w Mockito IMO.
bryant
Złożyłem sprawę przeciwko Hamcrestowi, aby dodać coś takiego. Zobacz github.com/mockito/mockito/issues/356
Mark
Czy to dla Mockito 1? Otrzymuję różne błędy kompilacji podczas próby kompilacji w wersji 2.10.
Frans
@Frans wygląda na to, że wydanie 2 było nadal w wersji beta, kiedy pisałem tę odpowiedź, więc tak, prawdopodobnie zostało napisane dla Mockito v1.10.19 lub mniej więcej. ( github.com/mockito/mockito/releases ) Prawdopodobnie można go zaktualizować ... :-D
Peter Westmacott
3

Używałem kodu w odpowiedzi Petera Westmacotta, jednak z Mockito 2.2.15 możesz teraz wykonać następujące czynności:

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

gdzie arg1, arg2, arg3są varargi.

znak
źródło
1

Opierając się na odpowiedzi Topchefa,

W wersji 2.0.31-beta musiałem użyć Mockito.anyVararg zamiast Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
NPike
źródło
3
dla informacji anyVararg jest teraz przestarzałe: „@deprecated od 2.1.0 użyj any ()”
alexbt
0

W moim przypadku sygnatura metody, którą chcę uchwycić jej argument to:

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

W tym przypadku powinieneś jawnie rzutować na tablicę bajtów :

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

Używam wersji mockito 1.10.19

Seyed Jalal Hosseini
źródło
0

Możesz także zapętlić argumenty:

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

na przykład sprawdź ich typy i rzuć je odpowiednio, dodaj do listy lub cokolwiek innego.

Richard Whitehead
źródło
0

Dostosowanie odpowiedzi z @topchef,

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

Zgodnie z dokumentacją java dla Mockito 2.23.4, Mockito.any () „Dopasowuje wszystko, w tym wartości null i varargs”.

Craig
źródło