【问题标题】:Can we use ArgumentCaptor to check log message in Mockito我们可以使用 ArgumentCaptor 检查 Mockito 中的日志消息吗
【发布时间】:2013-08-12 08:52:05
【问题描述】:

我在 ATG 中有一个表单处理程序,具有以下句柄方法 -

    public boolean handleFindCards(DynamoHttpServletRequest pRequest, DynamoHttpServletResponse pResponse)
        throws IOException, ServletException {

    Card card = cardService.getCard();

    if (card == null) {
         if (isLoggingError()) {
             logError("Card Service is null");
         }
    } else {
        // Other code
    }

    return checkFormRedirect(getSuccessURL(), getSuccessURL(), pRequest, pResponse);
}

表单处理程序正在使用其超类 GenericService 之一的日志记录。我想写一个测试来检查 cardService 是否为空。在上面的代码中,当卡服务为空时,它使用 logError 写入错误消息。我们可以在这里使用 ArgumentCaptor 来验证消息吗?或者有没有其他方法来测试这个场景?

在这里,模拟和间谍如何协同工作?我应该将获取卡片对象的调用存根 -

when(cardServiceMock.getCard()).thenReturn(cardMock);

这是我的测试方法-

    @Test
    public void testHandleCardServiceIfCardIsNull() throws IOException, ServletException {
        CardService cardServiceMock = mock(CardService.class);
        when(cardServiceMock.getCard()).thenReturn(null);

        CardOperationsFormHandler testObjSpy = spy(new CardOperationsFormHandler());
        testObjSpy.handleFindCards(requestMock, responseMock);
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        Mockito.verify(testObjSpy).logError(argumentCaptor.capture());
        Assert.assertEquals(argumentCaptor.getValue(), "Card Service is null");
    }

谢谢。

【问题讨论】:

  • logError 在超类决赛中吗?如果没有,您可以使用间谍。如果您需要澄清,请告诉我。
  • 超类不是最终的。我使用了 spy() 但收到错误为 [想要但未调用:实际上,与此模拟的交互为零。]
  • 我认为您需要先发布测试代码,然后才能弄清楚原因。

标签: mockito atg


【解决方案1】:

正如大卫华莱士所说,您需要使用Mockito.spy(T object)

所以用一个简单的方法:

public void runArgumentCaptor() {
    logError("This is an error");
}

测试可能如下所示:

    @Spy @InjectMocks private SomeClassName testObj;

    @Test(groups = { "unit" })
    public void runArgumentCaptorTest() throws Exception {

        testObj.runArgumentCaptor();

        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        Mockito.verify(testObj).logError(argumentCaptor.capture());
        Assert.assertEquals(argumentCaptor.getValue(), "This is an error");
    }

[编辑] 添加了testObj的声明。注意额外的@Spy。也更新了测试方法。

【讨论】:

  • 感谢 radimpe。这很好,但是模拟和间谍如何协同工作。在我的情况下,我的存根不工作,我得到空指针。我已经编辑了这个问题。请检查。
  • @Saurabh:添加@Spy注解,你可以同时做Mocking和Spying。
  • 我会用verify(testObj).logError("This is an error"); 替换你测试的最后三行——你真的不需要参数captor。
【解决方案2】:

它现在正在工作。正如 radimpe 所说,我已经用间谍代替了模拟。所以,我在这里创建 cardServiceSpy 而不是 cardServiceMock 然后使用存根。此外,需要使用 doReturn 进行存根。这是方法-

    @Test
    public void testHandleCardServiceIfCardIsNull() throws IOException, ServletException {
        CardService cardServiceSpy = spy(new CardService());
        doReturn(null).when(cardServiceSpy).getCard();

        CardOperationsFormHandler testObjSpy = spy(new CardOperationsFormHandler());
        testObjSpy.handleFindCards(requestMock, responseMock);
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        Mockito.verify(testObjSpy).logError(argumentCaptor.capture());
        Assert.assertEquals(argumentCaptor.getValue(), "Card Service is null");
    }

【讨论】:

  • 这比它需要的要复杂一些。 (1) 如果您使用 @Spy 注释,您可以避免显式实例化您正在监视的对象。 (2) 参数捕获器在这里毫无意义——您可以直接验证字符串参数。也不清楚您为什么要监视CardService 以及为什么要删除getCard()。你真的需要做这些事情吗?
  • @David,同意你的第一点。我正在对 getCard() 存根,因为它正在对 cardService 进行真正的调用并抛出空指针。
  • 真的吗?因为在您的测试方法的前两行之后没有使用cardServiceSpy。所以这两条线实际上什么也没做。您是否尝试在删除这两行的情况下运行此测试?另外,我强烈建议您将最后三行替换为我在 radimpe 的回答下给出的一行。我非常赞成进行非常简洁的测试,这样我就可以浏览测试类并准确地查看哪些已经测试过和哪些还没有测试过,而不会陷入噪音之中。
  • 谢谢大卫。我删除了两行,并按照您的建议将 ArgumentCaptor 替换为 verify 。我的测试运行良好。只有一件事,如果 cardService 会在某个地方使用,那么我们需要正确编写存根吗?
  • 这真的取决于CardService 的使用方式。有时,您会想要使用真实的;有时是模拟;有时是间谍。您需要考虑您实际尝试测试的内容,以及CardService 的功能是否与它相关。单元测试有时可能有点像一门艺术,但通过实践,你会变得更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多