Sprawdź wartość atrybutu obiektu za pomocą mockito

264

Mam wywołanie metody, które chcę wyśmiewać za pomocą mockito. Na początek stworzyłem i wstrzyknąłem instancję obiektu, na którym zostanie wywołana metoda. Moim celem jest zweryfikowanie jednego z obiektów w wywołaniu metody.

Czy istnieje sposób, w jaki mockito pozwala potwierdzić lub zweryfikować obiekt i jego atrybuty, gdy wywoływana jest metoda mock?

przykład

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

Zamiast tego anyObject()chcę sprawdzić, czy obiekt argumentu zawiera określone pola

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Priyank
źródło
Alternatywą dla użycia mockito w tych przypadkach, może być utworzenie niestandardowego kodu pośredniczącego, który rozszerza klasę mockedObject i zastępuje niektóreMethodOnMockedObject, aby zapisać obiekt do późniejszego porównania.
Gonen I

Odpowiedzi:

539

Nowa funkcja dodana do Mockito sprawia, że ​​jest to jeszcze łatwiejsze,

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

Zobacz dokumentację Mockito


W przypadku, gdy istnieje więcej niż jeden parametr i pożądane jest przechwytywanie tylko jednego parametru, użyj innych ArgumentMatchers, aby zawinąć pozostałe argumenty:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
iraSenthil
źródło
1
jeśli twoja metoda ma więcej niż jeden argument, musisz użyć Matchers również dla wszystkich innych argumentów. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa
1
Co jeśli jest wiele argumentów? Jak określasz dokładnie ten, który Cię interesuje?
IgorGanapolsky
2
@IgorGanapolsky Zakładając, że doSomething wymaga drugiego parametru String: Verify (mock) .doSomething (argument.capture (), anyString ());
GreenTurtle,
potrzeba używania dopasowań dla wszystkich argumentów jest wyłącznie zgodna ze standardową specyfikacją użycia dopasowywania „wszystko albo nic”.
Charney Kaye,
54

Myślę, że najłatwiejszym sposobem weryfikacji obiektu argumentu jest użycie refEqmetody:

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

Można go użyć, nawet jeśli obiekt nie implementuje equals(), ponieważ używane jest odbicie. Jeśli nie chcesz porównywać niektórych pól, po prostu dodaj ich nazwy jako argumenty refEq.

John29
źródło
1
to bardzo elegancki sposób, ale niestety org.mockito.Matchers jest teraz przestarzałe
ihebiheb
5
@ihebiheb Został przeniesiony do ArgumentMatchers
Michael
48

Jeszcze jedną możliwością, jeśli nie chcesz używać ArgumentCaptor(na przykład, ponieważ używasz również stubowania), jest użycie Hamcrest Matchers w połączeniu z Mockito.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
charleyc
źródło
2
Uwaga: upewnij się, że Matcherspakiet jest poprawny, ponieważ pisanie tego samego wiersza kodu z org.mockito.Matchersklasą wprowadza mylący wyjątek stwierdzający, że parametr funkcji próbnej po prostu nie pasuje.
buer
1
Należy pamiętać, że we współczesnych wersjach Mockito jest MockitoHamcrest.argThat()i nie jestMockito.argThat()
Roman Puchkovskiy
17

Jest to odpowiedź oparta na odpowiedzi z iraSenthil, ale z adnotacją ( Captor ). Moim zdaniem ma to kilka zalet:

  • jest krótszy
  • łatwiej jest czytać
  • może obsługiwać leki generyczne bez ostrzeżeń

Przykład:

@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
źródło
Będzie to działać tylko dla jednego argumentu w params.
IgorGanapolsky
Możesz użyć jednego porywacza dla więcej niż jednego argumentu. Jeśli przechwycisz więcej niż jeden argument, możesz wyświetlić listę wszystkich wyników captor.getAllValues(). Metoda captor.getValue()zastosowana w odpowiedzi daje ostatni wynik.
Walery Strauch
11

Jeśli używasz Java 8, możesz użyć wyrażeń Lambda, aby dopasować.

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

Przykładowe połączenie

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

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

Więcej informacji: http://source.coveo.com/2014/10/01/java8-mockito/

GuiSim
źródło
5

Powyższe rozwiązania tak naprawdę nie działały w moim przypadku. Nie mogłem użyć ArgumentCaptor, ponieważ metoda była wywoływana kilka razy i musiałem sprawdzić każdą z nich. Prosty Matcher z „argThat” z łatwością sobie poradził.

Custom 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)));
gwizdać
źródło
3

I bardzo ładne i czyste rozwiązanie w Koltinie z com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})
Cililing
źródło
1

Możesz odnieść się do następujących kwestii:

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

Spowoduje to sprawdzenie, czy metoda mockedObject jest wywoływana z pożądanymObject jako parametrem.

zaid bepari
źródło
1

Kolejny łatwy sposób to zrobić:

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
źródło
0

Jawadok dla refEq wspomniał, że kontrola równości jest płytka! Więcej informacji można znaleźć pod linkiem poniżej:

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

Problem z „płytką równością” nie może być kontrolowany, gdy używasz innych klas, które nie implementują metody .equals (), klasa „DefaultMongoTypeMapper” jest przykładem, w którym metoda .equals () nie jest zaimplementowana.

org.springframework.beans.factory.support oferuje metodę, która może wygenerować definicję komponentu bean zamiast tworzyć instancję obiektu, i można jej użyć do pozbycia się niepowodzenia porównania.

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

** „Definicja fasoli jest tylko opisem fasoli, a nie samą fasolą. Opisy fasoli poprawnie implementują equals () i hashCode (), więc zamiast tworzyć nową DefaultMongoTypeMapper (), zapewniamy definicję, która mówi wiosnie, jak to się dzieje powinien utworzyć „

W twoim przykładzie możesz zrobić coś takiego

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());
Saif Masadeh
źródło
0

Uproszczone rozwiązanie bez tworzenia nowej klasy implementacji Matcher i używania wyrażenia lambda:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
murali mohan
źródło