Mockito가 여러 번 호출되는 메소드의 인수를 캡처 할 수 있습니까?


446

두 번 호출되는 메소드가 있으며 두 번째 메소드 호출의 인수를 캡처하려고합니다.

내가 시도한 것은 다음과 같습니다.

ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()

그러나 TooManyActualInvocationsMockito doSomething는 한 번만 호출해야 한다고 생각 하므로 예외가 발생 합니다.

의 두 번째 호출의 인수를 어떻게 확인할 수 doSomething있습니까?

답변:


784

나는 그것이 있어야한다고 생각

verify(mockBar, times(2)).doSomething(...)

mockito javadoc의 샘플 :

ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());

List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());

3
doSomething()이것으로 각각의 개별 호출에서 전달 된 인수를 포착 할 수 있습니까 ?
matt b

36
이와 같은 작업을 수행하는 경우 Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);캡처 된 인수는 두 번 동일합니다 (실제로 동일한 사람 개체이므로). groups.google.com/forum/#!msg/mockito/capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane"참조하십시오. KBRocVedYT0 / 5HtARMl9r2wJ .
asmaier

2
이것은 좋지만 두 가지 다른 유형의 객체 호출을 어떻게 테스트 할 수 있습니까? 예를 들어 ExecutorService.submit (new MyRunableImpl ()); 그런 다음 ExecutorService.submit (new MyAnotherRunableImpl ())?
레온

@asmaier가 설명 한 사례를 처리 해야하는 경우 여기에 답변을 게시했습니다. stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
Leon의 질문에 대한 답이 여전히 궁금한 사람은 공통 기본 클래스 ( Runnable)를 사용하고 필요한 경우 캡처 된 인수에 대해보다 구체적인 유형 검사를 수행하십시오.
Matthew 읽기

50

Mockito 2.0부터 정적 메소드 Matchers.argThat (ArgumentMatcher) 를 사용할 수도 있습니다 . Java 8 덕분에 이제 훨씬 더 깨끗하고 읽기 쉽습니다.

verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));

더 낮은 Java 버전에 묶여 있다면 나쁘지 않습니다.

verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));

물론 그중 어느 것도 호출 순서를 확인할 수 없습니다 -InOrder 를 사용해야합니다 :

InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));

다음 과 같은 호출을 가능하게하는 mockito-java8 프로젝트를 살펴보십시오 .

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));

2
이것은 좋은 기술입니다. 나는 현재 다소 비밀스러운 결과를 얻고 있습니다 : "원하지만 호출되지 않았습니다 : / n mockAppender.append (<Index manager ut $$ lambda $ 5 9 / 1 3 1 9 5 1 0 1 6>);" -arg가 있습니다 CharSequence. "원하는"인수를 올바르게 인쇄하기위한 보고서를 얻는 방법을 알고 있습니까?
마이크 설치류

@mikerodent ArgumentMatcher <T>를 구현하는 클래스를 만드는 더 장황한 경로를 사용하면 암호 출력을 수정할 수 있습니다. 구현에서 toString 메서드를 재정의하면 mockito 테스트 출력에 원하는 모든 메시지가 제공됩니다.
노아 솔로몬

25

에 대한 모든 호출의 유효성을 검사하지 않으려면 doSomething()마지막 호출 만 확인하면됩니다 ArgumentCaptor.getValue(). Mockito javadoc 에 따르면 :

메소드가 여러 번 호출 된 경우 최신 캡처 된 값을 리턴합니다.

그래서 이것은 작동합니다 ( Foo방법이 있다고 가정 getName()) :

ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());

두 값을 모두 포착하는 방법이 있습니까?
Hars

9

@Captor 주석이 달린 ArgumentCaptor를 사용할 수도 있습니다. 예를 들면 다음과 같습니다.

@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@BeforeTest
public void init() {
    //Initialize objects annotated with @Mock, @Captor and @Spy.
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldCallAddMethodTwice() {
    mockedList.add("one");
    mockedList.add("two");
    Mockito.verify(mockedList, times(2)).add(argCaptor.capture());

    assertEquals("one", argCaptor.getAllValues().get(0));
    assertEquals("two", argCaptor.getAllValues().get(1));
}

6

Java 8의 람다에서 편리한 방법은

org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}

이전 방식보다 이것이 더 편리한 방법을 알 수 없습니다. 나는 람다를 잘 사용하는 것을 좋아하지만 이것이 람다인지 확실하지 않습니다.
Eric Wilson

0

우선 : 항상 mockito 정적을 가져와야합니다.이 방법으로 코드를 훨씬 더 읽기 쉽고 직관적으로 만들 수 있습니다. 아래 코드 샘플은 작동해야합니다.

import static org.mockito.Mockito.*;

verify () 메서드에서 ArgumentCaptor를 전달하여 테스트에서 실행을 보장하고 ArgumentCaptor를 통해 인수를 평가할 수 있습니다.

ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());

List<MyExampleClass> passedArguments = argument.getAllValues();

for (MyExampleClass data : passedArguments){
    //assertSometing ...
    System.out.println(data.getFoo());
}

테스트하는 동안 전달 된 모든 인수 목록은 argument.getAllValues ​​() 메소드를 통해 액세스 할 수 있습니다.

단일 (마지막으로 호출 된) 인수 값은 추가 조작 / 확인 또는 원하는 작업을 위해 argument.getValue ()를 통해 액세스 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.