Может ли Mockito проверить, что аргумент имеет определенные свойства/поля?
Скажем, я издеваюсь над этим классом Foo
class Foo {
public void doThing(Bar bar) {
// ...
}
}
и это Bar
class Bar {
private int i;
public int getI() { return i; }
public void setI(int i) { this.i = i; }
}
Я знаю, что могу использовать функцию проверки Mockito, чтобы узнать, был ли Foo#doThing(Bar)
на макет с конкретным экземпляром Bar
или любой Bar
с Mockito.any(Bar.class)
, но есть ли способ убедиться, что это было вызываемый любой Bar
но с определенным значением для i
или Bar#getI()
?
То, что я знаю, возможно:
Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);
Я хочу знать, есть ли способ проверить, что Bar
с определенными вещами, истинными в этом отношении, был передан в качестве аргумента.
Ответы
Ответ 1
В Mockito 2.1.0 и более поздних версиях Java 8 вы можете передавать лямбду-аргумент argThat из коробки, чтобы не требовалось сопоставления пользовательских аргументов. Для примера в ОП будет:
verify(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));
Это связано с тем, что в Mockito 2.1.0 ArgumentMatcher
является функциональным интерфейсом.
Ответ 2
Если вы используете Mockito 2.1.0 или выше и Java 8 или выше, см. Этот ответ, вместо этого он намного проще.
Я нашел ответ, когда писал вопрос.
Да, ты можешь. Вместо использования any(Bar.class)
вам нужно реализовать собственный экземпляр ArgumentMatcher<T>
и использовать Mockito#argThat(Matcher)
, например, мы хотим проверить, что i
5...
// in the test (could also be outside)
private static final class BarIs5 extends ArgumentMatcher<Bar> {
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == 5;
}
}
Затем verify(mockedFoo).doThing(argThat(new BarIs5()));
следующие действия: verify(mockedFoo).doThing(argThat(new BarIs5()));
Ударьте его с надстройкой, добавив параметры конструктора!
private static final class BarIsWhat extends ArgumentMatcher<Bar> {
private final int i;
public BarIsWhat(int i) {
this.i = i
}
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == i;
}
}
Затем verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));
следующие действия: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));
Обновление: это появилось в моей очереди из-за значка и увидело некоторое место для улучшения.
Я пробовал это, и он работает. Вы можете использовать выражение лямбда, которое намного чище (если вы не возражаете против непроверенных предупреждений о броске).
Единственная проблема в том, что argThat
принимает Hamcrest Matcher
, который не является @FunctionalInterface
. К счастью, Mockito ArgumentMatcher
является абстрактным классом, который расширяет его и имеет только один абстрактный метод.
В вашем тесте (или в каком-то общем месте) сделайте такой метод, как ниже
private static <T> ArgumentMatcher<T> matches(Predicate<T> predicate) {
return new ArgumentMatcher<T>() {
@SuppressWarnings("unchecked")
@Override
public boolean matches(Object argument) {
return predicate.test((T) argument);
}
};
}
Теперь в вашем тесте вы можете сделать это, чтобы использовать выражение лямбда:
verify(mockedFoo).doThing(argThat(matches( (Bar arg) -> arg.getI() == 5 )));
Ответ 3
Если вы не можете использовать Mockito 2+, вы также можете использовать старый добрый ArgumentCaptor
. Это будет немного более многословно, хотя:
ArgumentCaptor<Long> siteIdCaptor = ArgumentCaptor.forClass(Long.class);
verify(repository).findBySiteId(siteIdCaptor.capture());
assertEquals(15, siteIdCaptor.getValue().longValue());