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?


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

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

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


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?


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

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


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.


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.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.