使用mocks验证错误消息,其中结果可以根据id进行更改



所以我有一些单元测试,我在其中模拟了许多对象,并用它们来编写测试。

我正在使用anyInt()参数匹配器设置mock对象。我想验证一个名为displayErrorMessage(String errorMsg)的方法。此方法接受一个字符串,并将其输出到GUI中。它接受的字符串在发送到具有相关成员Id的显示方法之前进行格式化。

我想再次使用字符串格式的参数匹配器将正确的错误消息传递给verify语句。

String.format("Member %d cannot borrow at this time.", anyInt());

我知道anyInt()返回零。假设这样,我可以手动验证displayErrorMessage(),但这似乎不正确。

当前测试代码:

@Test
public void borrowingRestrictedWhenCardSwipedHasExceededFineLimit() throws Exception {
    when(memberDAO.getMemberByID(anyInt())).thenReturn(member);
    when(member.hasReachedFineLimit()).thenReturn(true);
    ctl.initialise();
    ctl.cardSwiped(anyInt());
    String errorMessage = "Member %d cannot borrow at this time.";
    errorMessage = String.format(errorMessage, anyInt());
    verify(ui).displayErrorMessage(errorMessage);
}

这种验证在这种情况下有效:

verify(ui).displayErrorMessage("Member 0 cannot borrow at this time.");

如果我处理得不对,有什么更好的方法呢?

由于这个解释有点长,这里有一个答案。。。

当需要匹配时,使用anyInt()作为参数。主要有两种情况:

when( myMock.someMethod( anyInt() ).thenReturn( x );

这使得每当调用someMethod时,myMock都会返回"x",而不管实际的int是什么。所以…

myMock.someMethod( 12 ) // will return x
myMock.someMethod( -180 ) // will return x
myMock.someMethod( 42 ) // will return x
// etc. ANY int parameter you give, will lead to x, since you told Mockito so in the when statement.

另一种使用方法是验证:

verify(myMock, times(1)).someMethod( anyInt() );

如果someMethod从未用任何int调用过,这只会引发错误。如果它是用12、-180、42、1、2或3调用的,等等。这就可以了(只要它只调用一次——不是每个int调用一次,而是总共调用一次)。

当然,anyInt()有一个int值,因为它必须放在int的位置,但我会完全忽略这个值,永远不要依赖它

当最后调用real方法时,不应该使用anyInt(),而应该使用一个real值,因为这会给你更多的控制权。在你的情况下,老实说,我根本不会使用anyInt():

@Test
public void borrowingRestrictedWhenCardSwipedHasExceededFineLimit() throws Exception {
    when(memberDAO.getMemberByID(42)).thenReturn(member);
    when(member.hasReachedFineLimit()).thenReturn(true);
    ctl.initialise();
    ctl.cardSwiped(42);
    String errorMessage = "Member %d cannot borrow at this time.";
    errorMessage = String.format(errorMessage, 42);
    verify(ui).displayErrorMessage(errorMessage);
}

为什么?因为通过这种方式,您可以确定,当调用ctl.cardSwiped时,它实际上会使用memberDAO.getMemberByID调用的参数。使用anyInt()不能。例如,ctl.cardSwiped:中可能存在错误

memberDao.getMemberById( parameter - 1);

有了anyInt(),你的结果仍然是会员。

您知道您的测试用例将给出42作为参数,并且只有当给出42时,您才希望返回成员。因此,您测试给定的参数是否实际用于内部(memberDao)对象的调用。

所以,至少调用ctl。使用实数而不是anyInt,最好甚至替换所有的anyInt()调用,因为它们实际上并不能改善您的测试用例,但实际上降低了测试的质量。

使用anyInt并不意味着测试证明任何int都可以工作。使用什么int是你的工作。例如,允许使用负整数吗?零等

编辑:由于它在另一个答案中被提及。。。是的,当然,另一种可能性是"静音"错误消息,它不需要实际的id。问题是:错误消息重要吗?那里的id重要吗?我想是的,但这取决于用例。因此,如果确切的错误消息很重要,则必须验证确切的错误信息,并在错误消息发生更改时调整测试。但这完全没关系,测试是为了确保特定的行为——如果确切的错误信息很重要,它是特定行为的一部分吗,必须进行测试。如果这种行为发生了变化,单元测试的工作就是大喊"Boo Hoo,那个方法不再做它应该做的事情了。"。这就是单元测试的目的。如果特定的错误台面不重要,但重要的是它包含id,那么您可以对照它进行验证(使用containsArgumentCaptor)。如果只调用错误方法很重要,而不管实际消息是什么,那么根据anyString()进行验证并使用它。

Answer接口可用于与mock及其调用方法的参数进行交互。

doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) throws Throwable {
        String msg = (String) args[0];
        //Asserting the arguments with which mock method was invoked
        Assert.assertEquals("expected", msg);
        return null;
    }
}).when(ui).displayErrorMessage(any(String.classs));

相关内容

  • 没有找到相关文章