Mockito:使用“thenReturn”中的方法返回模拟不起作用

2022-08-31 19:55:26

我遇到了我认为可能是Mockito的错误,但想知道是否有其他人可以解释为什么这个测试不起作用。

基本上,我有两个对象,就像这样:

public class FirstObject {
    private SecondObject secondObject;
    public SecondObject getSecondObject() { return secondObject; }
}

public class SecondObject {
    private String name;
    public String getName() { return name; }
}

第一个对象通过注释和前面的方法进行模拟:

@Mock
FirstObject mockedFirstObject;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

第二个对象在方法中被模拟:

public SecondObject setupMockedSecondObject() {
    SecondObject secondObject = Mockito.mock(SecondObject.class);
    Mockito.when(secondObject.getName()).thenReturn("MockObject");
    return secondObject;
}

当包含对此方法的直接调用以设置和获取第二个对象的模拟时,它将失败:thenReturn

@Test
public void notWorkingTest() {
    Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
    Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}

但是,当由相同方法返回的模拟被分配给一个局部变量时,该局部变量用于 ,它的工作原理是:thenReturn

@Test
public void workingTest() {
    SecondObject mockedSecondObject = setupMockedSecondObject();
    Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
    Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}

我们是做错了什么,还是这确实是Mockito中的错误/限制?这是否是故意的不工作原因?


答案 1

这确实是Mockito的局限性,并且在他们的FAQ中引用了它:

我可以内联吗?thenReturn()mock()

很遗憾,你不能这样做:

when(m.foo()).thenReturn(mock(Foo.class));
//                         ^

原因是,如果我们允许上述构造,则检测未完成的存根将不起作用。我们认为是框架验证的“权衡”(另请参阅前面的FAQ条目)。但是,您可以稍微更改代码以使其正常工作:

//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);

如前所述,解决方法是将所需的返回值存储在局部变量中,就像您所做的那样。

我的理解是,Mockito每次调用其方法时都会验证您对它的用法。在正在进行的存根过程中调用另一个方法时,您将中断其验证过程。


答案 2

您不能在 中使用方法,但可以在 您的代码将在出现条件时之后调用,这与基于以下任何解决方法不同:thenReturnthenAnswerthenReturn

因此,你可以写:

@Test
public void nowWorkingTest() {
    Mockito.when(mockedFirstObject.getSecondObject()).thenAnswer(new Answer<Map>() {
        @Override
        public Map answer(InvocationOnMock invocation) {
            return setupMockedSecondObject();
        }
    });
    Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}

让我们在这里找到另一个例子


推荐