我正在使用Mockito来测试我的 class 。我尝试使用Deep存根,因为我没有办法在Mockito的另一个模拟对象内注入Mock。

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()方法时,在这种情况下它将调用real方法。
when(somehelper.invokeMeth(isA(RequestObject.class)))
    .thenCallRealMethod();

在这种情况下,我期望webServiceTemplate不为null。

但是,当代码尝试执行该行时,我得到了一个N​​ullpointer异常
webServiceTemplate.marshalSendAndReceive(t);

有什么线索可以访问深层模拟对象(即模拟中的模拟-在这种情况下为somehelper模拟中的webserviceTemplete模拟),然后应用when条件抛出WebserviceIOException吗?
我想要这样做,以便可以测试MyService.create()来检查当WebServiceIOException抛出代码时它的行为是否正确。

最佳答案

是的,当然,您正在混合真实对象和模拟对象。加上像部分模拟一样使用thenCallRealMethod lloks,在这里感觉很不对劲,也就怪怪javadoc of this method也谈到了这一点。

与设计明智地相比,我的最终确定应该给您带来压力,拥有返回模拟的模拟通常是一种气味。更准确地说,您违反了 Demeter律,或者不遵循告诉,不要问原则。

任何查看您的代码的人都不会理解为什么该代码需要模拟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));
}

那些如何寻找您的实际用例?

希望有帮助

10-06 02:47