如何告诉Mockito模拟对象在下次调用时返回不同的内容?


202

因此,我正在像这样在类级别上将模拟对象创建为静态变量...在一个测试中,我想Foo.someMethod()返回某个值,而在另一个测试中,我希望它返回一个不同的值。我遇到的问题是,似乎我需要重建模拟才能使其正常工作。我想避免重建模拟,只在每个测试中使用相同的对象。

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.

    }

}

在第二个测试中,当调用testObj.bar()时,我仍然收到0作为值。解决此问题的最佳方法是什么?请注意,我知道我可以Foo在每个测试中使用不同的模拟,但是,我必须从链接多个请求mockFoo,这意味着我必须在每个测试中进行链接。

Answers:


43

首先,不要使模拟静态。将其设为私有字段。只需将您的setUp类放在@Beforenot中@BeforeClass。它可能会运行很多,但很便宜。

其次,您现在拥有的方式是获取模拟以根据测试返回不同内容的正确方法。


438

您也可以存根连续调用(2.8.9 API中的#10)。在这种情况下,您将使用多个thenReturn调用或一个具有多个参数(varargs)的thenReturn调用。

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
我认为您还可以利用.thenReturn()使用varargs的事实,因此代码可以简化为:when(mockFoo.someMethod())。thenReturn(0,1,-1);
贾斯汀·穆勒

10
@JustinMuller-我认为(相对于评论),这是一个单独的答案
Brian Agnew

16
在这种情况下,此答案不正确。如果将此方法存根返回0和1,那么只要再运行test1再运行,就可以了test2。但是,您的持续集成环境可能会以其他顺序运行测试。或者可能是您希望自己运行test2而不test1先运行,在这种情况下它将失败。单元测试必须始终彼此独立;并且各个测试之间绝对不应存在​​依赖关系,也不应依赖于特定的测试顺序。而链接thenReturn语句...
达伍德·伊本·卡里姆

4
...具有用途,就像对单个变量使用argargs一样,thenReturn在这种特殊情况下,这不是正确的解决方案。在我看来,这里的大批支持者最有可能无法理解这个问题。
达伍德·伊本·卡里姆

2
如果没有@FixMethodOrder
罗杰(Roger)

29

对于所有搜索返回某项然后再次调用抛出异常的人:

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

要么

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


14

对于使用spy()和doReturn()而不是when()方法的任何人:

您需要在不同的调用上返回不同的对象的是这样的:

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

对于经典模拟:

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

或抛出异常:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.