Mockito. Проверьте аргументы метода

220

Я гуглил по этому поводу, но не нашел ничего актуального. У меня есть что-то вроде этого:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

Теперь я хочу убедиться, что то mymethod(Object o), что вызывается внутри runtestmethod(), было вызвано с Объектом o, а не с любым другим. Но я всегда сдаю тест, независимо от того, что я ставлю на проверку, например, с помощью:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

или

Mockito.verify(mock.mymethod(Mockito.eq(null)));

или

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

Я всегда сдаю тест. Как я могу выполнить эту проверку (если это возможно)?

Спасибо.

manolowar
источник

Ответы:

337

Альтернатива ArgumentMatcherесть ArgumentCaptor.

Официальный пример:

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

Захват также можно определить с помощью аннотации @Captor :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
источник
1
Спасибо за образец! Никогда не использовал это. Кажется немного странным иметь в коде такие вещи, как captor , но это помогло.
Артемида
1
Хаха, я не понял вопроса, но ответ мне очень помог. Спасибо :-)
Маркус К.
13
Важно: вызовите verify () / capture () после использования макета. Я думал, что это должно быть "установлено" раньше ...
Даниэль Олдер
1
Спасибо за этот ответ!
Хосе Флавио Киспе Ирразабаль
Это отличный ответ !! Большое спасибо!
Ульки Игорь
61

Вы пытаетесь сделать логическое равенство, используя метод объекта .equals? Вы можете сделать это, используя argThat matcher, который включен в Mockito

import static org.mockito.Matchers.argThat

Затем вы можете реализовать свой собственный механизм сопоставления аргументов, который будет откладываться для каждого метода объектов .equals.

private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
    T thisObject;

    public ObjectEqualityArgumentMatcher(T thisObject) {
        this.thisObject = thisObject;
    }

    @Override
    public boolean matches(Object argument) {
        return thisObject.equals(argument);
    }
}

Теперь, используя свой код, вы можете обновить его, чтобы прочитать ...

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj)).thenReturn(null);

Testeable obj = new Testeable();
obj.setMockeable(mock);
command.runtestmethod();

verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));

Если вы просто хотите получить ТОЧНОЕ равенство (тот же объект в памяти), просто сделайте

verify(mock).mymethod(obj);

Это подтвердит, что он был вызван один раз.

Мэтью Киркли
источник
1
Вы можете использовать встроенный ReflectionEqualsкласс для этих целей.
takacsot
2
+1 за ваш ответ. Но я хотел бы добавить, что verify(mock).mymethod(obj);не проверяет на точное равенство (тот же объект в памяти). Вместо этого он использует метод equals объекта, который мог бы быть переопределен.
efux
Вы также можете создать анонимную реализацию, ArgumentMatcherчтобы быть менее многословным.
Botchniaque
1
Более подробно: по умолчанию verify()вызывается метод / входящего аргумента / equals(), а не / записанный объект / equals()метод. это не имеет значения, если вы не пытаетесь подтвердить, что ваш тестовый объект возвращает конкретный экземпляр объекта, а субъект возвращает вместо этого то, что должно быть прозрачным декоратором этого экземпляра. В verifyаргументе equals()не будет знать , декоратор; в то время как декораторы equals()будут переписаны, чтобы терпеть оригинал. В этом случае ваш тест будет ошибочно провален.
Марк МакКенна
54
  • Вам не нужно eqсовпадение, если вы не используете другие сопоставители.
  • Вы не используете правильный синтаксис - ваш вызов метода должен быть вне .verify(mock). Теперь вы начинаете проверку результата вызова метода, ничего не проверяя (не вызывая метод). Следовательно, все тесты проходят.

Ваш код должен выглядеть так:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
источник
Я пробовал это раньше, и снова сейчас, чтобы быть уверенным. У меня все та же проблема, тест всегда проходит.
manolowar
2
Это по ссылке
cnexans
17

argThat плюс лямбда

вот как вы можете провалить проверку аргументов:

    verify(mock).mymethod(argThat(
      (x)->false
    ));

где

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

argThat плюс утверждает

вышеупомянутый тест "скажет" Expected: lambda$... Was: YourClass.toSting.... Вы можете получить более конкретную причину сбоя, если использовать утверждения в лямбда-выражении:

    verify(mock).mymethod(argThat( x -> {
      assertThat(x).isNotNull();
      assertThat(x.description).contains("KEY");
      return true;
    }));

НО: ЭТО ТОЛЬКО РАБОТАЕТ С 1 МЕТОДОМ ВЫЗОВА. Если проверенный метод вызывается 2+ раза, mockito передает все вызываемые комбинации каждому верификатору. Таким образом, mockito ожидает, что ваш верификатор молча вернется trueдля одного набора аргументов и false(без исключений assert) для других допустимых вызовов. Это ожидание не является проблемой для 1 вызова метода - оно должно просто возвращать true 1 раз.

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

Теперь тест говорит: Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. ПРИМЕЧАНИЕ. Я использовал assertJутверждения, но вам решать, какую платформу утверждений использовать.


argThat с несколькими аргументами.

Если вы используете argThat, все аргументы должны быть снабжены совпадениями. Например:

    verify(mock).mymethod(eq("VALUE_1"), argThat((x)->false));
    // above is correct as eq() is also an argument matcher.

verify(mock).mymethod("VALUE_1", argThat((x)->false));

// above is incorrect; an exceptoin will be thrown, as the fist arg. is given without an argument matcher.

где:

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

eq согласовани

самый простой способ проверить, равен ли аргумент:

verify(mock).mymethod(eq(expectedValue));
// NOTE:   ^ where the parentheses must be closed.

прямой аргумент

если сравнение по ссылке приемлемо, то продолжайте с:

verify(mock).mymethod(expectedArg);
// NOTE:   ^ where the parentheses must be closed.

Первопричина первоначального отказа вопроса была не в то место из paranthes: verify(mock.mymethod.... Это было неправильно. Право будет:verify(mock).*

EPoX
источник
1
Это мой любимый ответ, работает и гораздо элегантнее, чем другие.
Airwavezx
11

Я использовал Mockito.verify таким образом

@UnitTest
public class JUnitServiceTest
{
    @Mock
    private MyCustomService myCustomService;


    @Test
    public void testVerifyMethod()
    {
       Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
       Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
       Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
       Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
       Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
       Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
    }
}
Свободомыслящих
источник
5

Вы проверили метод equals для надёжного класса? Если этот метод возвращает всегда значение true или вы проверяете один и тот же экземпляр на одном и том же экземпляре и метод равных не перезаписывается (и поэтому проверяет только ссылки), он возвращает значение true.

рит
источник
4

Другой метод заключается в использовании метода org.mockito.internal.matchers.Equals.Equals вместо его переопределения:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Нильс Рено
источник
3

Вы пробовали это с тем же () совпадением? Как в:

verify(mockObj).someMethod(same(specificInstance));

У меня такая же проблема. Я пробовал это с помощью eq () matcher, а также refEq (), но у меня всегда были ложные срабатывания. Когда я использовал тот же () matcher, тест не удался, когда аргументы были разными экземплярами, и проходил, когда аргументы были одним и тем же экземпляром.

cbbcloud
источник
-1

Вы также можете использовать TypeSafeDiagnosingMatcher

    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {

        StringBuilder text = new StringBuilder(500);

        @Override
        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
            String productCode = req.getPackageIds().iterator().next().getValue();
            if (productCode.equals(request.getSupplierProductCode())) {
                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                return true;
            }

            text.append(req.toString());
            return false;
        }

        @Override
        public void describeTo(Description d) {
            d.appendText(text.toString());
        }
    };
}

Затем проверьте этот вызов:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
источник