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?


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

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

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


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.


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

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


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.


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


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.