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

Pol*_*878 183 java junit unit-testing mocking mockito

所以,我正在创建一个模拟对象作为类级别的静态变量,如此...在一个测试中,我想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.

    }

}
Run Code Online (Sandbox Code Playgroud)

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

Ton*_*y R 412

您还可以发送连续呼叫(2.8.9 api中的#10).在这种情况下,您将使用多个thenReturn调用或一个thenReturn调用多个参数(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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想你也可以利用.thenReturn()接受varargs的事实,所以代码可以缩短为:when(mockFoo.someMethod()).thenReturn(0,1,-1); (162认同)
  • 在这种情况下,这个答案不是正确的做法.如果你将这个方法存根以返回0和1,那么只要运行`test1`然后运行`test2`就可以了.但可能是您的持续集成环境将以其他顺序运行测试.或者你可能想要自己运行`test2`,而不先运行`test1`,在这种情况下它会失败.单元测试_must_总是彼此独立; 并且应该_never_是各个测试之间的依赖关系,或者依赖于特定的测试顺序.而链接`thenReturn`声明...... (16认同)
  • @JustinMuller - 我认为这是值得一个单独的答案(而不是评论) (9认同)
  • ...有它的用途,就像使用varargs用于单个`thenReturn`一样,在这种特殊情况下它不是正确的解决方案.在我看来,这里的成群人很可能没有理解这个问题. (4认同)
  • 如果没有`@ FixMethodOrder`,Junit本身并不能确保测试订单 (2认同)

sho*_*639 43

首先不要让模拟静态.使它成为一个私人领域.只需将你的setUp类放入@Beforenot @BeforeClass.它可能会运行一堆,但它很便宜.

其次,你现在拥有它的方法是让模拟根据测试返回不同的东西的正确方法.


Nag*_*ila 18

对于所有搜索返回内容然后再调用另一个调用抛出异常的人:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));
Run Code Online (Sandbox Code Playgroud)

要么

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));
Run Code Online (Sandbox Code Playgroud)


小智 10

或者,甚至更清洁:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
Run Code Online (Sandbox Code Playgroud)

  • 这应该是答案。 (4认同)

fl0*_*l0w 9

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

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

doReturn(obj1).doReturn(obj2).when(this.client).someMethod();
Run Code Online (Sandbox Code Playgroud)

对于经典模拟:

when(this.client.someMethod()).thenReturn(obj1, obj2);
Run Code Online (Sandbox Code Playgroud)