Пример аргумента Mockito в аргументе Captor


152

Может ли кто-нибудь предоставить мне пример, показывающий, как использовать org.mockito.ArgumentCaptorкласс и чем он отличается от простых сопоставителей , которые предоставляются с mockito.

Я прочитал предоставленные документы mockito, но они не иллюстрируют это ясно, никто из них не может объяснить это с ясностью.

Ответы:


203

Я согласен с тем, что сказал @fge, более того. Давайте посмотрим на пример. Считайте, что у вас есть метод:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

Теперь, если вы хотите проверить внутренние данные, вы можете использовать захват:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);

Если doSomething(data)мутирует innerData, то будет ли это изменение присутствовать assertEquals("Some inner data", actual.innerData)или будет innerDataзафиксировано как есть перед doSomething выполнением?
Кори Кляйн

@CoryKlein The OtherClass- это макет, и, как он определен сейчас, на самом деле doSomething()ничего делать не будет, он просто записывает переданный объект. Это означает, что он будет записан как есть перед doSomethingвыполнением.
Слава Шпитальный

3
В verify, times(1)по умолчанию и может быть опущено.
Inego

откуда ArgumentCaptor знает, что произошло foo (other), поскольку он создается только после вызова foo (other)?
AvramPop

2
@AvramPop тот, кто знает, что это фиктивный объект. Он содержит внутри много информации о макете. Внутри всей этой информации он также содержит историю вызовов для каждого метода с его параметрами. Когда вы вызываете verifyметод, он использует эту информацию для сопоставления с проверкой, которую вы выполняете. Для каждого параметра он спрашивает, соответствует ли он конкретному проверяемому вызову. Когда установлен флажок ArgumentCaptor, он просто сохраняет значения, с которыми он был вызван, поэтому по verifyзавершении он содержит все соответствующие вызовы. Это примерно так, как это работает. Надеюсь, это поможет
Слава Шпитальный

37

Два основных отличия:

  • когда вы фиксируете даже один аргумент, вы можете проводить гораздо более сложные тесты для этого аргумента и с более очевидным кодом;
  • ArgumentCaptorможет захватить более чем один раз.

Чтобы проиллюстрировать последнее, предположим, что у вас есть:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

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

Фактически, это или любое количество аргументов, поскольку a VerificationModeне ограничивается фиксированным количеством вызовов; в любом случае похититель предоставит вам доступ ко всем из них, если вы пожелаете.

Это также имеет то преимущество, что такие тесты (imho) намного проще писать, чем реализовывать свои собственные ArgumentMatchers, особенно если вы комбинируете mockito с assertj.

О, и, пожалуйста, подумайте об использовании TestNG вместо JUnit.


1
Что, если в метод передано несколько параметров - все разных типов? Например, как вы на самом деле проверяете, что логический параметр был истинным .
Игорь Ганапольский

24
Не могли бы вы объяснить свой комментарий: О, и, пожалуйста, подумайте об использовании TestNG вместо JUnit. . Зачем это учитывать? Зачем менять?
Navigatron 09

1
@IgorGanapolsky, вы просто добавляете еще один ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Майкл Майкл = новый Майкл (); michael.sayHi (j); verify (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("значение равно" + arg.getValue ()); System.out.println ("строка равна" + arg2.getValue ());
johnwick0831

15

Шаги для полной проверки:

Подготовьте похитителя:

ArgumentCaptor<SomeArgumentClass> someArgumentCaptor = ArgumentCaptor.forClass(SomeArgumentClass.class);

проверить, что вызов зависит от компонента (соавтора тестируемого объекта) раз (1), является значением по умолчанию, поэтому его необходимо добавить.

verify(dependentOnComponent, times(1)).send(someArgumentCaptor.capture());

times (1) - значение по умолчанию, поэтому добавлять его не нужно.

Передать аргумент соавтору

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument может использоваться для утверждений


-2

Здесь я даю вам правильный пример одного метода обратного вызова. Итак, предположим, что у нас есть такой метод, как метод login ():

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

Я также поместил сюда весь вспомогательный класс, чтобы сделать пример более понятным: класс loginService

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

и у нас есть слушатель LoginListener как:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

теперь я просто хотел протестировать метод login () класса Login

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

также не забудьте добавить аннотацию над тестовым классом как

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)

1
Разве это не должно относиться к ArgumentCaptor?
Фелипе Мартинс Мело

да, мы захватываем слушателя, переданного методу login () в примере входа в систему (LoginProvider loginProvider, обратный вызов LoginListener)
Викрам Сингх

Где captorопределено в вашем ответе?
tom_mai78101

ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Викрам Сингх,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.