我想验证字符的具体顺序,以确保它们不会出现乱码。我尝试使用InOrder
编写它,但它似乎不起作用,或者至少在Mockito 1.8.5中。
@Test
public void inOrderTest() throws IOException{
final String message = "Hello World!\n";
for( char c : message.toCharArray() )
mockWriter.write( c );
final InOrder inOrder = inOrder( mockWriter );
for( char c : message.toCharArray() )
inOrder.verify( mockWriter ).write( c );
inOrder.verifyNoMoreInteractions();
}
上述测试失败并显示以下消息:
Verification in order failure:
mockWriter.write(108);
Wanted 1 time:
-> at org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:62)
But was 3 times. Undesired invocation:
-> at org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:58)
如何为此编写Mockito测试?
编辑:归档为错误http://code.google.com/p/mockito/issues/detail?id=296
答案 0 :(得分:19)
我向以前的受访者道歉;但在我看来,使用答案在Mockito的一个基本思想面前稍微过了一点,即存根和验证是两个完全独立的过程。 Mockito具有存根功能和验证功能,Mockito的制造商已经努力将这两者分开。答案是用于存根;而在某些情况下,答案是最好的验证方式,我不相信这是其中之一。
我会使用ArgumentCaptor而不是Answer。我会在测试类中编写这样的方法,然后以“Hello world”作为参数调用它。请注意,我没有对此进行测试,因此可能包含拼写错误。
private void verifyCharactersWritten( String expected ){
ArgumentCaptor<Character> captor = ArgumentCaptor.forClass( Character.class );
verify( mockWriter, times( expected.length())).write( captor.capture());
assertEquals( Arrays.asList( expected.toCharArray()), captor.getAllValues());
}
希望这有帮助。
答案 1 :(得分:4)
顺序验证是一个单独的概念,与完成某些事情的次数有关,因此当你到达'l'并告诉Mockito验证它发生时,它会通过有序检查,但是因为'l'而失败'打了三次电话,你(含蓄地)告诉它只有一次。这是我之前在Mockito中遇到的一个怪癖,但几乎任何时候它都会发生,我最终决定我的测试写得不好,当我修复它时,问题就消失了。在你的情况下,我会说方式过度杀死以验证写入Writer的每个字符。如果要验证是否正确发送了消息,则应将输入消息与输出消息进行比较。在您的示例中,这可能需要使用StringWriter而不是模拟编写器。然后测试结束就像
assertThat(stringWriter.toString(), equalTo(message));
如果你真的要做你正在做的事情,我所能提出的建议就是深入研究Mockito代码,看看是否有办法实现它,并可能提交错误报告,看看他们对此有什么看法。 / p>
答案 2 :(得分:2)
Mockito的工作原理是按顺序验证和定期验证之间的一致性。换句话说,如果我们没有以这种方式实现它,那么API会以不同的方式令人惊讶:)在尝试设计一个体面的API时,你会做出权衡。
所以......答案。首先,您应该避免测试代码中的循环(或条件)之类的语句。原因是您对测试代码的清晰度和可维护性非常关注! =)
如果我们从测试中删除循环,我们不再有用例,但是......如果没有用例,很难给出答案。大卫的ArgumentCaptor可能不是一个坏主意。
希望有所帮助!
答案 3 :(得分:0)
我目前正在使用自定义答案进行黑客攻击。
final List<Integer> writtenChars = new ArrayList<>();
willAnswer(
new Answer(){
@Override
public Object answer( final InvocationOnMock invocation )throws Throwable {
final int arg = (int) invocation.getArguments()[0];
writtenChars.add( arg );
return null;
}
}
).given( mockWriter ).write( anyInt() );
然后在运行所需的方法之后,我针对列表对照预期的字符串进行测试。
final Iterator<Integer> writtenCharItr = writtenChars.iterator();
for( int charInt : "Hello World!\n".toCharArray() )
assertThat( charInt, is( writtenCharItr.next() ) );
assertThat( "There are no more chars.", writtenCharItr.hasNext(), is(false) );
verify( mockWriter ).flush();
如果您对多次方法调用感兴趣,除非您在列表中记录哪个方法被调用等,否则这不起作用。
编辑:向Brice道歉,您似乎已经独立地使用StringBuilder
代替List
独立而且更好地解决了此解决方案,但对于一般情况列表效果更好。
答案 4 :(得分:0)
这是一个奇怪的测试,但仍然应该受到模拟API的支持。我相信Mockito可以支持 ,因为其他模拟API确实支持它。
使用 Unitils Mock :
Mock<Writer> mockWriter;
@Test
public void inOrderTest() throws Exception {
Writer writer = mockWriter.getMock();
final String message = "Hello World!\n";
for (char c : message.toCharArray())
writer.write(c);
for (char c : message.toUpperCase().toCharArray())
mockWriter.assertInvokedInSequence().write(c);
MockUnitils.assertNoMoreInvocations();
}
或者使用 JMockit (我自己的工具):
@Test
public void inOrderTest(final Writer mockWriter) throws Exception {
final String message = "Hello World!\n";
for (char c : message.toCharArray())
mockWriter.write(c);
new FullVerificationsInOrder() {{
for (char c : message.toCharArray())
mockWriter.write(c);
}};
}
答案 5 :(得分:0)
每当使用inOrder
时,相同的调用需要与其他调用之间进行验证(其他方法或具有其他参数的相同方法),则可以使用Mockito.calls(1)
代替times(1)