Mockito có thể nắm bắt các đối số của một phương thức được gọi là nhiều lần không?


446

Tôi có một phương thức được gọi hai lần và tôi muốn nắm bắt đối số của lệnh gọi phương thức thứ hai.

Đây là những gì tôi đã thử:

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

Nhưng tôi có một TooManyActualInvocationsNgoại lệ, vì Mockito nghĩ rằng doSomethingchỉ nên gọi một lần.

Làm thế nào tôi có thể xác minh đối số của cuộc gọi thứ hai doSomething?

Câu trả lời:


783

Tôi nghĩ nó nên như vậy

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

Mẫu từ 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
Bạn có thể nắm bắt các đối số được truyền đến doSomething()trong mỗi lần gọi riêng biệt với điều này không?
matt b

36
Cần lưu ý rằng trong trường hợp bạn làm điều gì đó như thế này: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);đối số bị bắt sẽ giống nhau hai lần (vì thực tế đó là cùng một đối tượng người), vì vậy capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane", hãy xem thêm nhóm.google.com/forum/#!msg/mockito/ KBRocVedYT0 / 5HtARMl9r2wJ .
asmaier

2
Điều này là tốt, nhưng làm thế nào tôi có thể kiểm tra hai cách gọi đối tượng được gõ khác nhau? Ví dụ: ExecutorService.submit (MyRunableImpl ()) mới; và sau đó ExecutorService.submit (MyAnotherRunableImpl ()) mới?
Leon

Nếu một người cần xử lý trường hợp được mô tả bởi @asmaier, tôi đã đăng câu trả lời tại đây: stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
Đối với bất kỳ ai vẫn thắc mắc về câu trả lời cho câu hỏi của Leon, bạn sẽ sử dụng lớp cơ sở chung ( Runnable) và, nếu cần, hãy thực hiện kiểm tra loại cụ thể hơn về đối số đã bắt.
Matthew đọc

50

Vì Mockito 2.0 cũng có khả năng sử dụng phương thức tĩnh Matchers.argThat (ArgumentMatcher) . Với sự trợ giúp của Java 8, giờ đây nó dễ viết hơn và dễ đọc hơn:

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

Nếu bạn bị ràng buộc với phiên bản Java thấp hơn thì cũng không tệ lắm:

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

Tất nhiên không ai trong số họ có thể xác minh thứ tự các cuộc gọi - mà bạn nên sử dụng 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")));

Vui lòng xem dự án mockito-java8 để thực hiện các cuộc gọi như:

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

2
Đây là một kỹ thuật tốt đẹp. Tôi hiện đang nhận được một số đầu ra khá khó hiểu: "Muốn nhưng không được gọi: / n mockAppender.append (<Index manager ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);" - đối số có a CharSequence. Bạn có biết cách nào để báo cáo in ra đối số "muốn" đúng không?
mike gặm nhấm

@mikerodent Đầu ra mật mã có thể được sửa nếu bạn đi theo con đường dài hơn để tạo một lớp thực hiện ArgumentMatcher <T>. Ghi đè phương thức toString trong triển khai của bạn sẽ cung cấp bất kỳ thông báo nào bạn muốn trong đầu ra thử nghiệm mockito.
Nô-ê Solomon

25

Nếu bạn không muốn xác thực tất cả các cuộc gọi đến doSomething(), chỉ cuộc gọi cuối cùng, bạn chỉ có thể sử dụng ArgumentCaptor.getValue(). Theo javadoc Mockito :

Nếu phương thức được gọi nhiều lần thì nó trả về giá trị được bắt gần nhất

Vì vậy, điều này sẽ làm việc (giả sử Foocó một phương pháp 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());

Có cách nào để nắm bắt cả hai giá trị?
Hars

9

Bạn cũng có thể sử dụng @Captor chú thích ArgumentCaptor. Ví dụ:

@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

Với lambdas của Java 8, một cách thuận tiện là sử dụng

org.mockito.invocation.InvocationOnMock

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

Tôi không thể thấy cách này thuận tiện hơn cách cũ. Tôi thích sử dụng lambdas tốt, nhưng tôi không chắc đây có phải là một.
Eric Wilson

0

Trước hết: bạn phải luôn nhập mockito tĩnh, theo cách này, mã sẽ dễ đọc hơn (và trực quan) - các mẫu mã dưới đây yêu cầu nó hoạt động:

import static org.mockito.Mockito.*;

Trong phương thức xác minh (), bạn có thể vượt qua ArgumentCaptor để đảm bảo thực thi trong thử nghiệm và ArgumentCaptor để đánh giá các đối số:

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

Danh sách tất cả các đối số được thông qua trong quá trình kiểm tra của bạn có thể truy cập được thông qua phương thức argument.get ALLValues ​​().

Giá trị của đối số (được gọi cuối cùng) có thể truy cập được thông qua đối số.getValue () để thao tác / kiểm tra thêm hoặc bất cứ điều gì bạn muốn làm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.