所以我有一些单元测试,我在其中模拟了许多对象,并用它们来编写测试。
我正在使用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,那么您可以对照它进行验证(使用contains
或ArgumentCaptor
)。如果只调用错误方法很重要,而不管实际消息是什么,那么根据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));