【问题标题】:Mockito - deep stubbingMockito - 深度存根
【发布时间】:2012-11-21 16:50:49
【问题描述】:

我正在使用 Mockito 来测试我的课程。我正在尝试使用 Deep stubbing,因为我没有办法将 Mock 注入 Mockito 中的另一个模拟对象中。

class MyService{

    @Resource
    SomeHelper somehelper;

    public void create()
    {
        //....
        somehelper.invokeMeth(t);
    }
}

class SomeHelper{
    @Resource
    private WebServiceTemplate webServiceTemplate;

    public void invokeMeth(T t)
    {
        try{
            //...
            webServiceTemplate.marshalSendAndReceive(t);
        }catch (final WebServiceIOException e) {
            throw new MyAppException("Service not running");
        }
    }
}

现在我正在尝试对 MyService 类的 create() 方法进行单元测试。 我已经为 SomeHelper 注入了一个模拟如下

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
SomeHelper somehelper;

我现在想要的是,当在模拟的 somehelper 对象上调用 invokeMeth() 方法时,它在这种情况下调用真正的方法。

when(somehelper.invokeMeth(isA(RequestObject.class)))
    .thenCallRealMethod();

我期待 webServiceTemplate 在这种情况下不为空。

但是,当代码尝试执行该行时,我得到一个 Nullpointer 异常

webServiceTemplate.marshalSendAndReceive(t);

任何线索我可以访问一个深度模拟对象(即模拟中的模拟 - 在本例中为 somehelper 模拟中的 webserviceTemplete 模拟),然后应用 when 条件抛出 WebserviceIOException ? 我想要这个,以便我可以测试 MyService.create() 以检查当 WebServiceIOException 被抛出代码时它的行为是否正常。

【问题讨论】:

    标签: java junit mockito


    【解决方案1】:

    是的,当然,您正在混合真实对象和模拟。再加上使用thenCallRealMethod lloks 就像一个部分模拟,这里感觉不对,难怪javadoc of this method 也会谈论这个。

    我肯定应该强调你,而不是设计明智的,有一个返回一个模拟的模拟通常是一种气味。更准确地说,您违反了 Demeter 法则,或者没有遵循 Tell, Don't Ask 原则。

    任何查看您的代码的人都不知道为什么代码需要模拟WebServiceTemplate。您想对MyService 进行单元测试,但我看不到与WebServiceTemplate 的关系。相反,您应该只关注与您的助手的交互。并且单元测试SomeHelper 单独在那里您将能够检查SomeHelperWebServiceTemplate 之间的交互。

    这是我如何看待事物的一个小例子:

    public void ensure_helper_is_used_to_invoke_a_RequestObject() {
      // given a service that has an helper collaborator
      ... other fixture if necessary
    
      // when
      myService.behaviorToTest();
    
      // then
      verify(someHelperMock).invokeMeth(isA(RequestObject.class));
    }
    

    那些寻找你真正用例的人怎么样?

    希望有帮助

    【讨论】:

    • 谢谢布莱斯。是的,我完全同意你的看法。问题是,如果说该服务不可用,该服务位于代码层次结构的下一层,MyService create() 会以特定方式处理它。我同意我可以模拟错误响应并进行测试。这就是正确的单元测试。实际上正在探索是否需要我们可以在另一个模拟中注入一个模拟,然后在注入的模拟上应用一个when-then-return规则。
    • @Soumya '实际上是在探索是否需要我们在另一个 mock 中注入一个 mock,然后在注入的 mock 上应用 when-then-return 规则。' 不不能这样做,从技术上讲这是可能的,但它会破坏模拟语义,相反您可以使用 spy,但请注意,在您的情况下,它可能会再次导致部分模拟。最重要的是使测试易于维护且可读性强(富有表现力但不冗长);如果玩太多模拟,可能会影响目标;我做过一次,结果一团糟。
    • 非常感谢布莱斯。同意你的看法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多