使用模拟验证错误消息,其中结果可能会根据ID

时间:2015-10-28 08:20:41

标签: java unit-testing junit mockito junit4

所以我有一些单元测试,我在嘲笑一些对象,并使用它们来编写测试。

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

我想以String格式再次使用参数匹配器将正确的错误消息传递给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.");

如果我接近这个错误,那么更好的办法是什么?

2 个答案:

答案 0 :(得分:1)

由于这个解释稍微长一点,这里有一个答案......

当需要匹配时,

anyInt()用作参数。主要有两种情况:

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

这使myMock在调用someMethod时返回“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() );

如果不使用任何int调用someMethod,则只会抛出错误。如果它被调用12或-180或42或1或2或3等,这将是正常的(只要它被称为一次 - 每个int不一次,但总共一次。)

当然anyInt()有一个int值,因为它必须放在int的位置,但我会完全忽略该值而不再依赖它。

最后调用真实方法时,不应该使用anyInt()而应该使用实际值,因为这样可以提供更多控制。在你的情况下,我根本不会使用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都可以工作。什么内注使用是你的工作要知道。例如,是否允许使用负数?零?等

编辑:因为它已经在另一个答案中提出了......是的,当然另一种可能性是“愚蠢地”错误信息,它不需要实际的ID。问题是:错误消息是否重要?那里的身份重要吗?我猜是的,但这取决于用例。因此,如果确切的错误消息很重要,您将必须验证确切的错误消息 - 并在错误消息发生更改时调整您的测试。但这完全没问题,测试是为了确保特定的行为 - 如果确切的错误消息很重要,它是否是特定行为的一部分,必须进行测试。如果这种行为发生变化,那么单元测试的工作就是喊“Boo-Hoo,那种方法不再做它应该做的了。”。这就是单元测试的用途。 如果特定错误消息不重要但重要的是它包含id,那么您可以对其进行验证(使用containsArgumentCaptor)。 如果重要的是调用错误方法,无论实际消息如何,那么验证anyString()并完成它。

答案 1 :(得分:0)

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)); 接口可用于与模拟以及调用其方法的参数进行交互。

{{1}}