Làm thế nào để nói với một đối tượng giả Mockito trả lại một cái gì đó khác vào lần tiếp theo nó được gọi?


202

Vì vậy, tôi đang tạo một đối tượng giả như một biến tĩnh ở cấp độ như vậy ... Trong một thử nghiệm, tôi muốn Foo.someMethod()trả về một giá trị nhất định, trong khi trong một thử nghiệm khác, tôi muốn nó trả về một giá trị khác. Vấn đề tôi gặp phải là dường như tôi cần phải xây dựng lại các bản giả để làm cho nó hoạt động chính xác. Tôi muốn tránh xây dựng lại các bản giả, và chỉ sử dụng cùng một đối tượng trong mỗi bài kiểm tra.

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

Trong thử nghiệm thứ hai, tôi vẫn nhận được 0 là giá trị khi testObj.bar () được gọi là ... Cách tốt nhất để giải quyết vấn đề này là gì? Lưu ý rằng tôi biết tôi có thể sử dụng một giả khác nhau Footrong mỗi thử nghiệm, tuy nhiên, tôi phải loại bỏ nhiều yêu cầu mockFoo, nghĩa là tôi phải thực hiện chuỗi trong mỗi thử nghiệm.

Câu trả lời:


43

Trước hết đừng làm cho giả tĩnh. Làm cho nó một lĩnh vực tư nhân. Chỉ cần đặt lớp setUp của bạn vào @Beforekhông @BeforeClass. Nó có thể được chạy một loạt, nhưng nó rẻ.

Thứ hai, cách bạn có nó ngay bây giờ là cách chính xác để có được một bản giả để trả lại một cái gì đó khác nhau tùy thuộc vào bài kiểm tra.


438

Bạn cũng có thể Stub các cuộc gọi liên tiếp (số 10 trong 2.8.9 api). Trong trường hợp này, bạn sẽ sử dụng nhiều cuộc gọi thenReturn hoặc một cuộc gọi thenReturn với nhiều tham số (varargs).

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}

171
Tôi nghĩ bạn cũng có thể tận dụng thực tế là .thenReturn () có các varargs, vì vậy mã có thể được rút ngắn thành: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Justin Muller

10
@JustinMuller - tôi nghĩ đó là một câu trả lời riêng biệt (trái ngược với nhận xét)
Brian Agnew

16
Câu trả lời này không phải là điều chính xác để làm trong trường hợp này. Nếu bạn vẫn còn phương thức này để trả về 0 và 1, thì bạn sẽ ổn miễn là bạn chạy test1và sau đó test2. Nhưng nó có thể là môi trường tích hợp liên tục của bạn sẽ chạy các bài kiểm tra theo thứ tự khác. Hoặc có thể là bạn sẽ muốn tự chạy test2, mà không chạy test1trước, trong trường hợp đó sẽ thất bại. Các bài kiểm tra đơn vị phải luôn độc lập với nhau; và không bao giờ nên có sự phụ thuộc giữa các thử nghiệm riêng lẻ hoặc phụ thuộc vào một thứ tự thử nghiệm cụ thể. Trong khi các thenReturntuyên bố xâu chuỗi ...
Dawood ibn Kareem

4
... có những công dụng của nó, cũng như sử dụng varargs cho một lần duy nhất thenReturn, đó không phải là một giải pháp chính xác trong trường hợp cụ thể này. Dường như với tôi rằng đám người lên ngôi ở đây rất có thể đã không hiểu được câu hỏi.
Dawood ibn Kareem

2
Bản thân Junit không đảm bảo thứ tự kiểm tra mà không có@FixMethodOrder
Roger

29

Đối với tất cả những người tìm kiếm để trả lại một cái gì đó và sau đó cho một cuộc gọi ngoại lệ khác:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));

hoặc là

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));

19

Hoặc, thậm chí sạch hơn:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);

2
Đây nên là câu trả lời.
Ikthiander

14

Đối với bất kỳ ai sử dụng spy () và doReturn () thay vì phương thức when ():

những gì bạn cần để trả về đối tượng khác nhau trên các cuộc gọi khác nhau là:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

Đối với giả cổ điển:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

hoặc với một ngoại lệ bị ném:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
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.