Проверьте значение атрибута объекта с помощью mockito


264

У меня есть вызов метода, который я хочу издеваться над mockito. Для начала я создал и внедрил экземпляр объекта, для которого будет вызван метод. Моя цель - проверить один из объектов в вызове метода.

Есть ли способ, которым mockito позволяет утверждать или проверять объект и его атрибуты при вызове метода mock?

пример

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

Вместо того, чтобы делать, anyObject()я хочу проверить, что объект аргумента содержит некоторые конкретные поля

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

В качестве альтернативы использованию mockito в этих случаях вы можете рассмотреть возможность создания пользовательской заглушки, которая расширяет класс mockedObject и переопределяет someMethodOnMockedObject, чтобы сохранить объект для последующего сравнения.
Гонен, я

Ответы:


540

Новая функция, добавленная в 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, что вам нужно сделать: verify (mock) .doSomething (arguments.capture (), anyString ());
GreenTurtle

необходимость использовать сопоставления для всех аргументов исключительно в соответствии со стандартной спецификацией использования сопоставления «все или ничего».
Чарни Кей

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классом вызывает ложное исключение, утверждающее, что параметр фиктивной функции просто не совпадает.
Буер

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

Это будет работать только для одного аргумента в params.
Игорь Ганапольский

Вы можете использовать один захват для более чем одного аргумента. Если вы захватите более одного аргумента, вы можете просмотреть список всех результатов с помощью captor.getAllValues(). Метод, captor.getValue()который используется в ответе, дает последний результат.
Валерий Штраух

11

Если вы используете Java 8, вы можете использовать лямбда-выражения для соответствия.

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, так как метод вызывался несколько раз, и мне нужно было проверить каждый из них. Простой Matcher с "argThat" сделал свое дело легко.

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

Тест бегун

// 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 с требуемым объектом в качестве параметра.


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

Javadoc для 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 предлагает метод, который может генерировать определение компонента вместо создания экземпляра объекта, и его можно использовать, чтобы избавиться от ошибки сравнения.

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

** "Определение компонента - это только описание компонента, а не сам компонент. Описания компонентов должным образом реализуют equals () и hashCode (), поэтому вместо создания нового DefaultMongoTypeMapper () мы предоставляем определение, которое сообщает Spring, как оно должен создать один

В вашем примере вы можете сделать что-то вроде этого

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.