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

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

Спасибо.

Ответы:


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

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

Я пробовал это раньше, и снова сейчас, чтобы быть уверенным. У меня все та же проблема, тест всегда проходит.
— 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).*


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, тест не удался, когда аргументы были разными экземплярами, и проходил, когда аргументы были одним и тем же экземпляром.


-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)));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.