Sử dụng Mockito với nhiều cuộc gọi đến cùng một phương thức với cùng một đối số


289

Có cách nào để có một phương thức sơ khai trả về các đối tượng khác nhau trong các lần gọi tiếp theo không? Tôi muốn làm điều này để kiểm tra các phản ứng không xác định từ một ExecutorCompletionService. tức là để kiểm tra rằng bất kể thứ tự trả về của các phương thức, kết quả vẫn không đổi.

Mã tôi đang tìm kiếm để kiểm tra trông giống như thế này.

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}

Câu trả lời:


254

Bạn có thể làm điều đó bằng cách sử dụng thenAnswerphương thức (khi kết nối với when):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

Hoặc sử dụng doAnswerphương thức tĩnh, tương đương :

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();

634

Làm thế nào về

when( method-call ).thenReturn( value1, value2, value3 );

Bạn có thể đặt bao nhiêu đối số tùy thích trong ngoặc của thenReturn, miễn là tất cả chúng đều đúng loại. Giá trị đầu tiên sẽ được trả về lần đầu tiên phương thức được gọi, sau đó là câu trả lời thứ hai, v.v. Giá trị cuối cùng sẽ được trả về nhiều lần khi tất cả các giá trị khác được sử dụng hết.


4
Điều này sẽ làm việc với một giả, nhưng không phải với một điệp viên. Nếu bạn cần ngăn không gọi phương thức ban đầu, bạn cần doAnswer (...). Khi (someSpy) .someMethod (...).
Yuri

6
@Yuri - không hẳn. Bạn không cần doAnswerhoặc viết một Answertrường hợp mà bạn đề cập. Bạn chỉ có thể sử dụng doReturn(...).when(someSpy).someMethod(...). Có vẻ hợp lý khi cho rằng Emma quan tâm đến giả, hơn là gián điệp, nhưng tôi đoán tôi có thể thêm một cái gì đó vào câu trả lời của mình để đánh vần điều này. Cảm ơn các bình luận.
Dawood ibn Kareem

@DawoodibnKareem cho phép nói với cuộc gọi đầu tiên tôi muốn trả về một giá trị và cho cuộc gọi thứ hai tôi muốn ném Ngoại lệ. Điều này có thể giải quyết như thế nào?
Rito

@Rito Vui lòng đọc câu trả lời của Volodymyr hoặc câu trả lời của Raystorm. Cả hai đều bao gồm trường hợp đó.
Dawood ibn Kareem

Thật là một câu trả lời vẻ vang.
wild_noth

151

Như trước đây đã chỉ ra gần như tất cả các cuộc gọi là chuỗi.

Vì vậy, bạn có thể gọi

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

Thông tin thêm trong Tài liệu của Mockito .


3
Rất hữu ích! Điều gì sẽ xảy ra lần thứ 4 mock.methodđược gọi trong ví dụ này? Tôi muốn một cái gì đó như, trở lại foo lần đầu tiên nhưng trả lại thanh cho TẤT CẢ phần còn lại.
javaPlease42

6
Mỗi lần gọi bổ sung trên giả sẽ trả về 'sau đó quay lại' hoặc cuối cùng 'sau đó' Rất hữu ích
Francois Lacoursiere

Cảm ơn bạn đã hướng dẫn tuyệt vời và đơn giản. Chưa bao giờ biết điều này cho đến bây giờ. Tôi đã vật lộn để tìm cách lấy lại hai kết quả khác nhau trên hai cuộc gọi giống hệt nhau. Tiết kiệm cho tôi hàng tấn thời gian.
CharlesC

68

Bạn thậm chí có thể xâu chuỗi các doReturn()phương thức như thế này

doReturn(null).doReturn(anotherInstance).when(mock).method();

dễ thương phải không :)


4

Tôi đã triển khai một MultipleAnswerlớp giúp tôi bỏ các câu trả lời khác nhau trong mỗi cuộc gọi. Đây là đoạn mã:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}

1
Bạn có thể khởi tạo đối tượng đó một cách ngắn gọn, đơn giản và dễ đọc không?
usr-local-ΕΨΗΕΛΩΝ

1

Theo sau có thể được sử dụng như một phương thức phổ biến để trả về các đối số khác nhau trên các lệnh gọi phương thức khác nhau. Điều duy nhất chúng ta cần làm là chúng ta cần truyền một mảng theo thứ tự các đối tượng sẽ được truy xuất trong mỗi cuộc gọi.

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

Ví dụ. getAnswerForSubsequentCalls(mock1, mock3, mock2);sẽ trả về đối tượng mock1 trong cuộc gọi đầu tiên, đối tượng mock3 trong cuộc gọi thứ hai và đối tượng mock2 trong cuộc gọi thứ ba. Nên dùng như thế when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); này gần giống vớiwhen(something()).thenReturn(mock1, mock3, mock2);


1

Liên quan đến câu trả lời của @ [Igor Nikolaev] từ 8 năm trước, bằng cách sử dụng một phần Answercó thể được đơn giản hóa bằng cách sử dụng biểu thức lambda có sẵn trong Java 8.

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

hoặc đơn giản hơn:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());

1

Phong cách BDD:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);

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.