首页 > 解决方案 > 如何验证具有特定参数的流畅接口模拟链

问题描述

在这被标记为重复之前,我已经浏览了我能找到的所有关于 SO 的主题,但我没有看到任何接近我想要实现的目标。

我正在为几个所谓的“流畅”接口编写单元测试,并且在验证方法调用链而又不会使其过于复杂和不可读的情况下遇到困难。也许,我以为我错过了一些东西,我只是想重新发明轮子。我该怎么做呢?

拥有一个模拟对象MyQuery mockedQuery,我想实现以下目标:

MyQuery nameQueryMock = Mockito.mock(MyQuery.class);
Mockito.doReturn(nameQueryMock).when(mockedQuery)
    .name("SPECIFIC NAME");
MyQuery addressQueryMock = Mockito.mock(MyQuery.class);
Mockito.doReturn(addressQueryMock).when(nameQueryMock)
    .address("SPECIFIC ADDRESS");
Mockito.doReturn(JOHN_QUERY_RESULT).when(addressQueryMock)
    .singleResult();

我希望做的更像是:

Mockito.when(mockedQuery.name("SPECIFIC NAME").address("SPECIFIC ADDRESS").singleResult()).thenReturn(JOHN_QUERY_RESULT);

但我发现这不是它的工作原理。我尝试使用 Mockito 提供的答案(RETURNS_DEEP_STUBS这是我尝试过的事情之一),但我发现它仅适用于文档中指出的链的最后一个方法调用。在我的情况下,我JOHN_QUERY_RESULT只需要在两个name()方法都通过“特定名称”并address()通过“特定地址”时返回。

我希望能够以更简洁的方式实现这一点,因为我的链包含更多方法,并且采用第一种方法会使我的测试重载,其中包含很多我希望避免的代码。 我在这里想念什么?

更新:

一些背景知识:有MyService一个方法提供了一个createQuery()返回新实例的方法MyQuery。该服务(MyService)用于我要测试的中,该类做了一些工作,但它利用(那些)查询来检索有关它必须做的事情的信息。因此,在测试类中模拟和ing 它,也使用模拟将允许我测试行为本身,消除 的内部实现,只需能够指定特定查询搜索的返回值。MyServiceInjectMyQueryMyQuery

标签: javamockingmockito

解决方案


根据您的背景信息,我提出了以下示例:(
因此这可能与您的实际情况完全不同)

假设您的 TestClass 有这样的方法

public String someMethod(String name, String address) {
    return service.createQuery().name(name).address(address).singleResult();
}

并且对它的测试看起来像这样:

@InjectMocks
TestClass testClass;

@Mock
MyService service;

@Test
public void test() {

    MyQuery query = Mockito.mock(service);
    when(service.createQuery()).thenReturn(query);

    Mockito.when(mockedQuery.name("SPECIFIC NAME")
                            .address("SPECIFIC ADDRESS")
                            .singleResult())
           .thenReturn("JOHN_QUERY_RESULT");

    Assert.assertEquals("JOHN_QUERY_RESULT", testClass.someMethod("SPECIFIC NAME", "SPECIFIC ADDRESS"));
}

就我而言,仅当 name() 方法通过“特定名称”并且 address() 都通过“特定地址”时,我才需要返回 JOHN_QUERY_RESULT。

那部分已经被测试覆盖了,只是因为如果模拟不匹配你会得到一个NullPointerException代替。

此外,这听起来像是与 的 UnitTest 无关的东西TestClass,但应该与MyQuery.


但是,让我们看一下测试。我们可以看到,测试的每一行都包含一个基于 a 的操作mock

我们通过该测试取得了什么成果?我们测试了TestClass吗?

不,我们可以直接模拟整个TestClass结果并直接返回结果。嘲笑TestClass是不好的。

所以我的回答是:没什么。

我们已经验证了 mockito 做了应该做的事情,并且链接方法调用有效,但这超出了良好测试的范围。


我们可以做些什么来改善这一点?

我想说这样的测试对于 UnitTest 来说并不是真正的,更不用说模拟了,相反我们应该专注于集成测试。

如果MyQuery#singleResult有一些 3rd 方交互,例如与数据库,那么这是可以模拟的。

但是最后我们仍然需要另一个集成测试来进行数据库交互。在那种情况下,我们最好限制自己只测试一些Exceptions通常难以重现的东西。


推荐阅读