Перевірте значення атрибута об'єкта за допомогою mockito


264

У мене є виклик методу, який я хочу знущатися з mockito. Для початку я створив і ввів екземпляр об'єкта, за яким буде викликаний метод. Моя мета - перевірити один із об'єктів у виклику методу.

Чи є спосіб, що mockito дозволяє стверджувати або перевіряти об'єкт і його атрибути, коли викликається метод макету?

приклад

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

Замість того, щоб робити, anyObject()я хочу перевірити, чи об'єкт аргументу містить деякі конкретні поля

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)

В якості альтернативи використанню mockito в цих випадках ви можете розглянути можливість створення спеціальної заглушки, яка розширює клас mockedObject і переосмислює деякийMethodOnMockedObject, щоб зберегти об'єкт для подальшого порівняння.
Gonen I

Відповіді:


539

Нова функція, додана до Mockito, робить це ще простіше,

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

Погляньте на документацію Mockito


У випадку, коли існує більше ніж один параметр, і потрібне захоплення лише одного парамуму, використовуйте інші ArgumentMatchers, щоб обернути решту аргументів:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());

1
якщо ваш метод має більше ніж один аргумент, ви повинні використовувати Matchers і для всіх інших аргументів. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa

1
Що робити, якщо є кілька аргументів? Як вказати саме те, що вас цікавить?
ІгорГанапольський

2
@IgorGanapolsky Припускаючи другий параметр String для doSomething, що вам потрібно зробити: перевірити (макет) .doSomething (argument.capture (), anyString ());
GreenTurtle

потреба у використанні матчерів для всіх аргументів винятково застосовується до стандартних специфікацій використання матчера з усіма або без них.
Charney Kaye

54

Я думаю, що найпростішим способом перевірки об’єкта аргументу є використання refEqметоду:

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

Він може бути використаний, навіть якщо об'єкт не реалізує equals(), оскільки використовується рефлексія. Якщо ви не хочете порівнювати деякі поля, просто додайте їх імена як аргументи refEq.


1
це дуже елегантний спосіб, але, на жаль, org.mockito.Matchers зараз застаріло
ihebiheb

5
@ihebiheb Перенесено в ArgumentMatchers
Майкл

48

Ще одна можливість, якщо ви не хочете використовувати ArgumentCaptor(наприклад, тому що ви також використовуєте заглушку), - це використовувати Hamcrest Matchers у поєднанні з Mockito.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));

2
Sidenote: переконайтеся, що Matchersпакет правильний, оскільки написання одного і того ж рядка коду з org.mockito.Matchersкласом видає оманливе виняток, вказуючи, що параметр функції макету просто не відповідає.
buer

1
Зверніть увагу, що в сучасних версіях Mockito, це є MockitoHamcrest.argThat()і немаєMockito.argThat()
Роман Пучковський

17

Це відповідь, заснована на відповіді iraSenthil, але з анотацією ( Captor ). На мою думку, це має деякі переваги:

  • це коротше
  • легше читати
  • він може обробляти дженерики без попереджень

Приклад:

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

Це буде працювати лише для одного аргументу в парамах.
ІгорГанапольський

Ви можете використовувати один captor для більш ніж одного аргументу. Якщо ви зафіксували більше одного аргументу, ви можете перелічити всі результати за допомогою captor.getAllValues(). Метод, captor.getValue()який використовується у відповіді, дає останній результат.
Валері Страуч

11

Якщо ви використовуєте Java 8, ви можете використовувати вирази Lambda, щоб відповідати.

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

Приклад виклику

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

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

Більше інформації: http://source.coveo.com/2014/10/01/java8-mockito/


5

Вищезгадані рішення насправді не працювали в моєму випадку. Я не міг використовувати ArgumentCaptor, оскільки метод був викликаний кілька разів, і мені потрібно було перевірити кожен. Простий Матч з "argThat" зробив трюк легко.

Спеціальний відповідник

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

Тест-бігун

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

3

І дуже приємний і чистий розчин у кольтині від com.nhaarman.mockito_kotlin

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

1

Ви можете посилатися на таке:

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

Це дозволить перевірити, чи називається метод mockedObject з параметром desireObject як параметром.


1

Ще один простий спосіб зробити це:

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

0

Явадок для refEq згадав, що перевірка рівності є дрібною! Детальнішу інформацію можна знайти за посиланням нижче:

[ https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq T,%20java.lang.String...) обвинений1]

Проблему "малої рівності" неможливо контролювати, коли ви використовуєте інші класи, які не реалізують метод .equals (), клас "DefaultMongoTypeMapper" є прикладом, коли метод .equals () не реалізований.

org.springframework.beans.factory.support пропонує метод, який може генерувати визначення bean замість створення примірника об'єкта, і його можна використовувати для позбавлення від провалу порівняння.

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

** "Визначення bean - це лише опис bean, а не саме bean. Описи bean належним чином реалізують рівняння () та hashCode (), тому замість створення нового DefaultMongoTypeMapper () ми надаємо визначення, яке весною розповідає, як це має створити "

У вашому прикладі ви можете зробити щось подібне

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());

0

Спрощене рішення, не створюючи новий клас реалізації Matcher та використовуючи лямбда-вираз:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.