Mockito. Überprüfen Sie die Methodenargumente

220

Ich habe darüber gegoogelt, aber nichts Relevantes gefunden. Ich habe so etwas:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

Jetzt möchte ich überprüfen, ob das mymethod(Object o), was im Inneren runtestmethod()aufgerufen wird, mit dem Objekt aufgerufen wurde o, nicht mit einem anderen. Aber ich bestehe den Test immer, was auch immer ich auf die Verifizierung stelle, zum Beispiel mit:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

oder

Mockito.verify(mock.mymethod(Mockito.eq(null)));

oder

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

Ich habe den Test immer bestanden. Wie kann ich diese Überprüfung durchführen (wenn möglich)?

Danke dir.

Manolowar
quelle

Antworten:

334

Eine Alternative zu ArgumentMatcherist ArgumentCaptor.

Offizielles Beispiel:

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Ein Captor kann auch mithilfe der Annotation @Captor definiert werden :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
quelle
1
Danke für die Probe! Ich habe es nie benutzt. Es fühlt sich ein bisschen komisch an, Dinge wie Captor im Code zu haben, aber es hat geholfen.
Artemis
1
Haha, ich habe die Frage nicht verstanden, aber die Antwort hat mir sehr geholfen. Danke :-)
Marcus K.
13
Wichtig: Rufen Sie verify () / capture () auf, nachdem Sie den Mock verwendet haben. Ich dachte, es muss "installiert" werden, bevor ...
Daniel Alder
1
Danke für diese Antwort!
Jose Flavio Quispe Irrazábal
Das ist eine großartige Antwort !! Vielen Dank!
Ulky Igor
61

Versuchen Sie, mit der .equals-Methode des Objekts eine logische Gleichheit herzustellen? Sie können dies mit dem in Mockito enthaltenen argThat-Matcher tun

import static org.mockito.Matchers.argThat

Als Nächstes können Sie Ihren eigenen Argument-Matcher implementieren, der die Methode .equals für jedes Objekt zurückstellt

private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
    T thisObject;

    public ObjectEqualityArgumentMatcher(T thisObject) {
        this.thisObject = thisObject;
    }

    @Override
    public boolean matches(Object argument) {
        return thisObject.equals(argument);
    }
}

Mit Ihrem Code können Sie ihn jetzt aktualisieren, um ...

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj)).thenReturn(null);

Testeable obj = new Testeable();
obj.setMockeable(mock);
command.runtestmethod();

verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));

Wenn Sie nur GENAUE Gleichheit anstreben (dasselbe Objekt im Speicher), tun Sie dies einfach

verify(mock).mymethod(obj);

Dadurch wird überprüft, ob es einmal aufgerufen wurde.

Matthew Kirkley
quelle
1
Sie können die eingebaute ReflectionEqualsKlasse für diese Zwecke verwenden.
Takacsot
2
+1 für deine Antwort. Aber ich möchte hinzufügen, dass verify(mock).mymethod(obj);nicht auf GENAUE Gleichheit geprüft wird (dasselbe Objekt im Speicher). Stattdessen wird die Methode equals-equals verwendet, die möglicherweise überschrieben wurde.
Efux
Sie können auch eine anonyme Implementierung erstellen ArgumentMatcher, um weniger ausführlich zu sein.
Botchniaque
1
Weitere Details: Standardmäßig wird verify()die equals()Methode / des eingehenden Arguments anstelle der Methode / des aufgezeichneten Objekts equals()aufgerufen. Dies ist irrelevant, es sei denn, Sie versuchen zu bestätigen, dass Ihr Testobjekt eine bestimmte Objektinstanz zurückgibt, und das Subjekt gibt stattdessen einen transparenten Dekorator dieser Instanz zurück. Die verifyArgumente equals()würden den Dekorateur nicht kennen; während der Dekorateur equals()umgeschrieben würde, um das Original zu tolerieren. In diesem Fall schlägt Ihr Test fälschlicherweise fehl.
Mark McKenna
54
  • Sie benötigen den eqMatcher nicht, wenn Sie keine anderen Matcher verwenden.
  • Sie verwenden nicht die richtige Syntax - Ihr Methodenaufruf sollte sich außerhalb von befinden .verify(mock). Sie initiieren jetzt die Überprüfung des Ergebnisses des Methodenaufrufs, ohne etwas zu überprüfen (ohne einen Methodenaufruf durchzuführen). Daher bestehen alle Tests.

Ihr Code sollte folgendermaßen aussehen:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
quelle
Ich hatte das schon einmal versucht und jetzt noch einmal, um sicher zu sein. Ich habe immer noch das gleiche Problem, der Test besteht immer.
Manolowar
2
Es verifeis durch Bezugnahme
cnexans
17

argThat plus Lambda

So können Sie Ihre Argumentprüfung nicht bestehen:

    verify(mock).mymethod(argThat(
      (x)->false
    ));

wo

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

argThat plus behauptet

der obige Test wird "sagen" Expected: lambda$... Was: YourClass.toSting.... Sie können eine spezifischere Ursache für den Fehler ermitteln, wenn Sie Asserts im Lambda verwenden:

    verify(mock).mymethod(argThat( x -> {
      assertThat(x).isNotNull();
      assertThat(x.description).contains("KEY");
      return true;
    }));

ABER: DAS FUNKTIONIERT NUR MIT 1 METHODENRUF. Wenn die verifizierte Methode mehr als 2 Mal aufgerufen wird, übergibt mockito alle aufgerufenen Kombinationen an jeden Verifizierer. Mockito erwartet daher, dass Ihr Verifizierer stillschweigend truefür eines der eingestellten Argumente und false(keine Assert-Ausnahmen) für andere gültige Aufrufe zurückgibt . Diese Erwartung ist für 1 Methodenaufruf kein Problem - sie sollte nur 1 Mal true zurückgeben.

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

Jetzt sagt der Test : Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. HINWEIS: Ich habe assertJAsserts verwendet, aber es liegt an Ihnen, welches Assertion-Framework verwendet werden soll.


argThat mit mehreren Argumenten.

Wenn Sie verwenden argThat, müssen alle Argumente mit Übereinstimmungen versehen sein. Z.B:

    verify(mock).mymethod(eq("VALUE_1"), argThat((x)->false));
    // above is correct as eq() is also an argument matcher.

verify(mock).mymethod("VALUE_1", argThat((x)->false));

// above is incorrect; an exceptoin will be thrown, as the fist arg. is given without an argument matcher.

wo:

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

eq Matcher

Der einfachste Weg, um zu überprüfen, ob das Argument gleich ist:

verify(mock).mymethod(eq(expectedValue));
// NOTE:   ^ where the parentheses must be closed.

direktes Argument

Wenn ein Vergleich mit ref akzeptabel ist, fahren Sie fort mit:

verify(mock).mymethod(expectedArg);
// NOTE:   ^ where the parentheses must be closed.

Die Wurzelursache für das Versagen der ursprünglichen Frage war die falsche Stelle der Klammern : verify(mock.mymethod.... Das war falsch. Das Recht wäre:verify(mock).*

Epox
quelle
1
Dies ist meine Lieblingsantwort, funktioniert und ist viel eleganter als die anderen.
Airwavezx
11

Ich habe Mockito.verify auf diese Weise verwendet

@UnitTest
public class JUnitServiceTest
{
    @Mock
    private MyCustomService myCustomService;


    @Test
    public void testVerifyMethod()
    {
       Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
       Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
       Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
       Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
       Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
       Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
    }
}
Freimütig
quelle
5

Haben Sie die Methode equals für die verspottbare Klasse überprüft? Wenn dieser immer true zurückgibt oder Sie dieselbe Instanz gegen dieselbe Instanz testen und die Methode same nicht überschrieben wird (und daher nur anhand der Referenzen überprüft wird), wird true zurückgegeben.

rit
quelle
4

Die andere Methode besteht darin, die Methode org.mockito.internal.matchers.Equals.Equals zu verwenden, anstatt eine neu zu definieren:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Nils Renaud
quelle
3

Haben Sie es mit demselben () Matcher versucht? Wie in:

verify(mockObj).someMethod(same(specificInstance));

Ich hatte das gleiche Problem. Ich habe es sowohl mit dem eq () Matcher als auch mit dem refEq () Matcher versucht, aber ich hatte immer falsch positive Ergebnisse. Wenn ich denselben () Matcher verwendet habe, ist der Test fehlgeschlagen, wenn die Argumente unterschiedliche Instanzen waren, und wurde bestanden, sobald die Argumente dieselbe Instanz waren.

cbbcloud
quelle
-1

Sie können auch TypeSafeDiagnosingMatcher verwenden

    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {

        StringBuilder text = new StringBuilder(500);

        @Override
        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
            String productCode = req.getPackageIds().iterator().next().getValue();
            if (productCode.equals(request.getSupplierProductCode())) {
                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                return true;
            }

            text.append(req.toString());
            return false;
        }

        @Override
        public void describeTo(Description d) {
            d.appendText(text.toString());
        }
    };
}

Überprüfen Sie dann diesen Aufruf:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
quelle