Überprüfen Sie den Objektattributwert mit mockito

264

Ich habe einen Methodenaufruf, den ich mit mockito verspotten möchte. Zunächst habe ich eine Instanz eines Objekts erstellt und eingefügt, für das die Methode aufgerufen wird. Mein Ziel ist es, eines der Objekte im Methodenaufruf zu überprüfen.

Gibt es eine Möglichkeit, mit mockito das Objekt und seine Attribute beim Aufruf der mock-Methode zu bestätigen oder zu überprüfen?

Beispiel

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>anyObject())

Anstatt dies zu tun, anyObject()möchte ich überprüfen, ob das Argumentobjekt einige bestimmte Felder enthält

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Priyank
quelle
In diesen Fällen können Sie alternativ zur Verwendung von mockito einen benutzerdefinierten Stub erstellen, der die Klasse von mockedObject erweitert und someMethodOnMockedObject überschreibt, um das Objekt für einen späteren Vergleich zu speichern.
Gonen I

Antworten:

539

Die neue Funktion von Mockito macht dies noch einfacher.

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

Schauen Sie sich die Mockito- Dokumentation an


Wenn mehr als ein Parameter vorhanden ist und nur ein einziger Parameter erfasst werden soll, verwenden Sie andere ArgumentMatcher, um die restlichen Argumente zu verpacken:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
iraSenthil
quelle
1
Wenn Ihre Methode mehr als ein Argument enthält, müssen Sie Matchers auch für alle anderen Argumente verwenden. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa
1
Was ist, wenn es mehrere Argumente gibt? Wie geben Sie genau das an, an dem Sie interessiert sind?
IgorGanapolsky
2
@IgorGanapolsky Angenommen, Sie müssen einen zweiten String-Parameter für doSomething ausführen: verify (mock) .doSomething (argument.capture (), anyString ());
GreenTurtle
Die Notwendigkeit, Matcher für alle Argumente zu verwenden, entspricht ausschließlich der Standard-All-or-None-Matcher-Verwendungsspezifikation.
Charney Kaye
54

Ich denke, der einfachste Weg, ein Argumentobjekt zu überprüfen, ist die Verwendung der folgenden refEqMethode:

Mockito.verify(mockedObject).someMethodOnMockedObject(Matchers.refEq(objectToCompareWith));

Es kann auch verwendet werden, wenn das Objekt nicht implementiert wird equals(), da Reflexion verwendet wird. Wenn Sie einige Felder nicht vergleichen möchten, fügen Sie einfach ihre Namen als Argumente für hinzu refEq.

John29
quelle
1
Das ist ein sehr eleganter Weg, aber leider ist org.mockito.Matchers jetzt veraltet
ihebiheb
5
@ihebiheb Es ist zu ArgumentMatchers verschoben
Michael
48

Eine weitere Möglichkeit, wenn Sie nicht verwenden möchten ArgumentCaptor(z. B. weil Sie auch Stubbing verwenden), ist die Verwendung von Hamcrest Matchers in Kombination mit Mockito.

import org.mockito.Mockito
import org.hamcrest.Matchers
...

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
charleyc
quelle
2
Nebenbemerkung: Stellen Sie sicher, dass das MatchersPaket korrekt ist, da das Schreiben derselben Codezeile mit der org.mockito.MatchersKlasse eine irreführende Ausnahme auslöst, die besagt, dass der Parameter der Scheinfunktion einfach nicht übereinstimmt.
Buer
1
Bitte beachten Sie, dass es in modernen Mockito-Versionen ist MockitoHamcrest.argThat()und nichtMockito.argThat()
Roman Puchkovskiy
17

Dies ist eine Antwort, die auf der Antwort von iraSenthil basiert, jedoch mit Anmerkungen ( Captor ). Meiner Meinung nach hat es einige Vorteile:

  • es ist kürzer
  • es ist leichter zu lesen
  • Es kann Generika ohne Warnungen verarbeiten

Beispiel:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest{

    @Captor
    private ArgumentCaptor<List<SomeType>> captor;

    //...

    @Test 
    public void shouldTestArgsVals() {
        //...
        verify(mockedObject).someMethodOnMockedObject(captor.capture());

        assertThat(captor.getValue().getXXX(), is("expected"));
    }
}
Walery Strauch
quelle
Dies funktioniert nur für ein einzelnes Argument in params.
IgorGanapolsky
Sie können einen Captor für mehr als ein Argument verwenden. Wenn Sie mehr als ein Argument erfassen, können Sie alle Ergebnisse mit auflisten captor.getAllValues(). Die captor.getValue()in der Antwort verwendete Methode liefert das letzte Ergebnis.
Walery Strauch
11

Wenn Sie Java 8 verwenden, können Sie passende Lambda-Ausdrücke verwenden.

import java.util.Optional;
import java.util.function.Predicate;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

public class LambdaMatcher<T> extends BaseMatcher<T>
{
    private final Predicate<T> matcher;
    private final Optional<String> description;

    public LambdaMatcher(Predicate<T> matcher)
    {
        this(matcher, null);
    }

    public LambdaMatcher(Predicate<T> matcher, String description)
    {
        this.matcher = matcher;
        this.description = Optional.ofNullable(description);
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object argument)
    {
        return matcher.test((T) argument);
    }

    @Override
    public void describeTo(Description description)
    {
        this.description.ifPresent(description::appendText);
    }
}

Beispielaufruf

@Test
public void canFindEmployee()
{
    Employee employee = new Employee("John");
    company.addEmployee(employee);

    verify(mockedDal).registerEmployee(argThat(new LambdaMatcher<>(e -> e.getName()
                                                                         .equals(employee.getName()))));
}

Weitere Informationen: http://source.coveo.com/2014/10/01/java8-mockito/

GuiSim
quelle
5

Die obigen Lösungen haben in meinem Fall nicht wirklich funktioniert. Ich konnte ArgumentCaptor nicht verwenden, da die Methode mehrmals aufgerufen wurde und ich jede überprüfen musste. Ein einfacher Matcher mit "argThat" hat den Trick leicht gemacht.

Benutzerdefinierter Matcher

// custom matcher
private class PolygonMatcher extends ArgumentMatcher<PolygonOptions> {
    private int fillColor;
    public PolygonMatcher(int fillColor) {
        this.fillColor = fillColor;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof PolygonOptions)) return false;
        PolygonOptions arg = (PolygonOptions)argument;
        return Color.red(arg.getFillColor()) == Color.red(fillColor)
                && Color.green(arg.getFillColor()) == Color.green(fillColor)
                && Color.blue(arg.getFillColor()) == Color.blue(fillColor);
    }
}

Test Runner

// do setup work setup
// 3 light green polygons
int green = getContext().getResources().getColor(R.color.dmb_rx_bucket1);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(green)));

// 1 medium yellow polygons
int yellow = getContext().getResources().getColor(R.color.dmb_rx_bucket4);
    verify(map, times(1)).addPolygon(argThat(new PolygonMatcher(yellow)));

// 3 red polygons
int orange = getContext().getResources().getColor(R.color.dmb_rx_bucket5);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(orange)));

// 2 red polygons
int red = getContext().getResources().getColor(R.color.dmb_rx_bucket7);
verify(map, times(2)).addPolygon(argThat(new PolygonMatcher(red)));
zischen
quelle
3

Und sehr schöne und saubere Lösung in koltin aus com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})
Cililing
quelle
1

Sie können Folgendes verweisen:

Mockito.verify(mockedObject).someMethodOnMockedObject(eq(desiredObject))

Dadurch wird überprüft, ob die Methode mockedObject mit dem gewünschten Objekt als Parameter aufgerufen wird.

zaid bepari
quelle
1

Ein weiterer einfacher Weg, dies zu tun:

import org.mockito.BDDMockito;    
import static org.mockito.Matchers.argThat;
import org.mockito.ArgumentMatcher;

BDDMockito.verify(mockedObject)
        .someMethodOnMockedObject(argThat(new ArgumentMatcher<TypeOfMethodArg>() {

            @Override
            public boolean matches(Object argument) {
                final TypeOfMethodArg castedArg = (TypeOfMethodArg) argument;

                // Make your verifications and return a boolean to say if it matches or not
                boolean isArgMarching = true;

                return isArgMarching;
            }
        }));
pierrefevrier
quelle
0

Der Javadoc für refEq erwähnte, dass die Gleichheitsprüfung flach ist! Weitere Details finden Sie unter dem folgenden Link:

[ https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq(T,%20java.lang.String...)[1]

Das Problem "flache Gleichheit" kann nicht gesteuert werden, wenn Sie andere Klassen verwenden, die die Methode .equals () nicht implementieren. Die Klasse "DefaultMongoTypeMapper" ist ein Beispiel, in dem die Methode .equals () nicht implementiert ist.

org.springframework.beans.factory.support bietet eine Methode, mit der eine Bean-Definition generiert werden kann, anstatt eine Instanz des Objekts zu erstellen, und die zum Entfernen von Vergleichsfehlern verwendet werden kann.

 genericBeanDefinition(DefaultMongoTypeMapper.class)
                        .setScope(SCOPE_SINGLETON)
                        .setAutowireMode(AUTOWIRE_CONSTRUCTOR)
                        .setLazyInit(false)
                        .addConstructorArgValue(null)
                        .getBeanDefinition()

** "Die Bean-Definition ist nur eine Beschreibung der Bean, keine Bean selbst. Die Bean-Beschreibungen implementieren equals () und hashCode () ordnungsgemäß. Anstatt einen neuen DefaultMongoTypeMapper () zu erstellen, geben wir eine Definition an, die spring sagt, wie es ist sollte eine erstellen "

In Ihrem Beispiel können Sie so etwas tun

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());
Saif Masadeh
quelle
0

Eine vereinfachte Lösung, ohne eine neue Matcher-Implementierungsklasse zu erstellen und den Lambda-Ausdruck zu verwenden:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
murali mohan
quelle