Wie man Varargs in Mockito richtig zusammenbringt

152

Ich habe versucht, eine Methode mit vararg-Parametern mit Mockito zu verspotten:

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

Dies funktioniert jedoch nicht, wenn ich dies stattdessen tue:

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

Dies funktioniert, obwohl ich das Argument varargs beim Stubben der Methode vollständig weggelassen habe.

Irgendwelche Hinweise?

qualidafial
quelle
Die Tatsache, dass das letzte Beispiel funktioniert, ist eher trivial, da es dem Fall entspricht, in dem keine varargs-Parameter übergeben wurden.
Topchef

Antworten:

235

Mockito 1.8.1 hat einen beliebigen Vararg () - Matcher eingeführt :

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

Siehe auch den Verlauf dazu: https://code.google.com/archive/p/mockito/issues/62

Bearbeiten Sie die neue Syntax nach dem Veralten:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
Spitzenkoch
quelle
26
anyVararg()hat Object als Rückgabetyp. Führen Sie ein explizites Casting durch, um es mit allen var arg-Typen (z. B. String ..., Integer ... usw.) kompatibel zu machen. Wenn Sie zum Beispiel haben doSomething(Integer number, String ... args), können Sie den Mock / Stub-Code mit so etwas wie machen when(mock).doSomething(eq(1), (String) anyVarargs()). Das sollte den Kompilierungsfehler beheben.
Psycho Punch
15
Für Informationen ist anyVararg jetzt veraltet: "@deprecated ab 2.1.0 use any ()"
alexbt
5
Matchersist jetzt veraltet, um einen Namenskonflikt mit der org.hamcrest.MatchersKlasse zu vermeiden, und wird wahrscheinlich in mockito v3.0 entfernt. Verwenden Sie ArgumentMatchersstattdessen.
JonyD
31

Eine etwas undokumentierte Funktion: Wenn Sie einen benutzerdefinierten Matcher entwickeln möchten, der mit vararg-Argumenten übereinstimmt, müssen Sie ihn implementieren, org.mockito.internal.matchers.VarargMatcherdamit er ordnungsgemäß funktioniert. Es ist eine leere Markierungsschnittstelle, ohne die Mockito Argumente beim Aufrufen einer Methode mit varargs unter Verwendung Ihres Matchers nicht korrekt vergleicht.

Beispielsweise:

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
quelle
7

Aufbauend auf Eli Levines Antwort ist hier eine allgemeinere Lösung:

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

}

Dann können Sie es mit den Array-Matchern von hamcrest folgendermaßen verwenden:

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

(Offensichtlich machen statische Importe dies lesbarer.)

Peter Westmacott
quelle
Nett. Dies sollte in Mockito IMO eingebaut werden.
Bryant
Ich habe ein Problem gegen Hamcrest eingereicht, um so etwas hinzuzufügen. Siehe github.com/mockito/mockito/issues/356
Mark
Ist das für Mockito 1? Beim Kompilieren gegen 2.10 werden verschiedene Kompilierungsfehler angezeigt.
Frans
@Frans es sieht so aus, als ob die Version 2 noch in der Beta war, als ich diese Antwort schrieb, also ja, sie wurde wahrscheinlich für Mockito v1.10.19 oder so ungefähr geschrieben. ( github.com/mockito/mockito/releases ) Es ist wahrscheinlich aktualisierbar ... :-D
Peter Westmacott
3

Ich habe den Code in Peter Westmacotts Antwort verwendet, aber mit Mockito 2.2.15 können Sie jetzt Folgendes tun:

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

Wo arg1, arg2, arg3sind Varargs?

Kennzeichen
quelle
1

Aufbauend auf der Antwort von topchef,

Für 2.0.31-beta musste ich Mockito.anyVararg anstelle von Matchers.anyVararrg verwenden:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
NPike
quelle
3
Für Informationen ist anyVararg jetzt veraltet: "@deprecated ab 2.1.0 use any ()"
alexbt
0

In meinem Fall lautet die Signatur der Methode, deren Argument ich erfassen möchte:

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

In diesem Fall sollten Sie das Byte-Array explizit umwandeln :

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

Ich benutze die Mockito-Version 1.10.19

Seyed Jalal Hosseini
quelle
0

Sie können auch die Argumente durchlaufen:

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

Überprüfen Sie beispielsweise ihre Typen und setzen Sie sie entsprechend um, fügen Sie sie einer Liste hinzu oder was auch immer.

Richard Whitehead
quelle
0

Anpassen der Antwort von @topchef,

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

Gemäß den Java-Dokumenten für Mockito 2.23.4 stimmt Mockito.any () mit allem überein, einschließlich Nullen und Varargs.

Craig
quelle