【问题标题】:mock nested method calls using mockito使用 mockito 模拟嵌套方法调用
【发布时间】:2017-06-03 14:17:29
【问题描述】:

我有 4 个类,让我们说 A、B、C、D,每个类都调用另一个类的方法。

现在我已经模拟了 A 类,并且想使用 mockito 模拟一个方法

A a = Mockito.mock(A.class);

并希望在递归方法调用中获得“foo”,例如

a.getB().getC().getD() 应该返回 "foo"

我试过了

when(a.getB().getC().getD()).thenReturn("foo");

但得到了 nullPointerException

然后我尝试了

doReturn("foo").when(a.getB().getC().getD());

然后我得到org.mockito.exceptions.misusing.UnfinishedStubbingException:

我知道我可以创建 B、C 和 D 的对象,甚至可以编写类似的东西

B b = mock(B.class) 或 A.setB(new B())

等等。

但我不能一次完成吗? 任何帮助将不胜感激。

【问题讨论】:

标签: java methods junit mocking mockito


【解决方案1】:

Abhijeet 的回答在技术上是正确的,但重要的是要理解:您不应该这样做。

您的“生产”代码严重违反了Law of Demeter:您的 A 类应该知道必须获得 B 才能获得 C 才能获得D.

这只会导致所有这些类之间的超级紧密耦合。不是个好主意。

从这个意义上说:您应该看到,您需要在这里做一些特殊的事情才能让您的测试正常工作,这表明您的生产代码做了一些不正常的事情。。 p>

因此,与其“修复”您的测试设置,不如考虑解决真正的问题。这就是您的生产代码设计

声明:getB().getC().getD() 不是“递归”调用;它更像是方法调用的“流畅”链接。如前所述:这不是一件好事。

【讨论】:

  • 我完全同意你的看法。但有时为了谋生,开发人员必须为遗留应用程序编写单元测试用例,并不是每个人都能幸运地再次编写生产代码。
  • 你对 OO 的规则过于严格了,这不是你可以深入研究的唯一方法。假设你有一个配置对象,你可以将你的配置分组到嵌套类中(即@ 987654323@,authentication)。因此,您可以使用嵌套属性提供对字段的流畅访问,例如configuration.connection.idlePingIntervalconfiguration.authentication.privateKey(忽略getter/setter)。这正是我的用例。这与 Demeter 无关,因为这是一个数据类,而不是行为介词。
  • 当您知道自己在做什么时,一切都很好。但很多人没有。他们做事是因为他们可以,没有人告诉他们“这会导致这个或那个问题,所以宁愿这样做”。别搞错了:当你开始对这些规则草率时,迟早你会一团糟。对于 6 个月后被丢弃的代码无关紧要,但对于存在 5 年或 10 年或更长时间的代码来说却很重要。
  • 根据我的经验,这些类型的危险紧密耦合设计只有在为它们编写了复杂的单元测试后才能安全测试。否则,您可能会更改难以发现的边缘情况,因为您在确定自己知道什么被破坏(如果有的话)之前就进行了积极的重构。我非常不同意您的测试代码应该像类本身一样不了解被测代码的内部工作原理。很多时候,模拟的整个功能是删除接口如何实现的实现细节。
【解决方案2】:

添加 RETURNS_DEEP_STUBS 成功了:

A a = Mockito.mock(A.class, Mockito.RETURNS_DEEP_STUBS);

【讨论】:

  • 或者,如果使用注释:@Mock(answer = Answers.RETURNS_DEEP_STUBS)
  • 来自 RETURNS_DEEP_STUBS 的文档:“有一天我在网上看到的好话:每次模拟返回模拟时,仙女就会死去。”
  • 澄清一下,在@Andrey 的评论中,answerAnswers 正是您应该输入的;这不是你的模拟变量/类(我浪费了几分钟才意识到这一点)!
【解决方案3】:

尝试创建每个嵌套对象的模拟,然后模拟每个对象调用的单个方法。

如果目标代码是这样的:

public Class MyTargetClass {

    public String getMyState(MyClass abc){

       return abc.getCountry().getState();
    }
}

然后为了测试这一行,我们可以创建每个嵌套对象的模拟,如下所示:

public Class MyTestCase{

@Mock
private MyTargetClass myTargetClassMock;

@Mock
private MyClass myclassMockObj;

@Mock
private Country countryMockObj;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

    @Test
    public void test01(){

       when(myclassMockObj.getCountry()).thenReturn(countryMockObj);
       when(countryMockObj.getState()).thenReturn("MY_TEST_STATE");
       Assert.assertEquals("MY_TEST_STATE", myTargetClassMock.getMyState(myclassMockObj));
    }
}

【讨论】:

    猜你喜欢
    • 2018-04-28
    • 2015-11-04
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多