Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/350.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在Mockito中测试具有相同参数的调用的特定顺序?_Java_Testing_Mocking_Mockito - Fatal编程技术网

Java 如何在Mockito中测试具有相同参数的调用的特定顺序?

Java 如何在Mockito中测试具有相同参数的调用的特定顺序?,java,testing,mocking,mockito,Java,Testing,Mocking,Mockito,我想验证字符的特定顺序,以确保它们不会被乱码。我试着用按顺序编写它,但它似乎不起作用,至少在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 InOr

我想验证字符的特定顺序,以确保它们不会被乱码。我试着用
按顺序编写它,但它似乎不起作用,至少在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测试



编辑:作为bug归档

按顺序验证是一个独立的概念,与某件事情的执行次数不同,因此当您到达“l”并告诉Mockito验证它是否发生时,它通过了按顺序检查,但失败了,因为“l”调用进行了三次,而您(隐式地)告诉它仅预期一次。这是我在Mockito中遇到过的一个怪癖,但几乎每次它发生时,我都会认为我的测试写得很糟糕,当我修复它时,问题就消失了。在你的情况下,我认为验证每个写给作者的字符是过分的。如果要验证消息是否正确发送,应将输入消息与输出消息进行比较。在您的示例中,这可能需要使用StringWriter而不是嘲笑编写器。那么你的测试结束的时候看起来就像

assertThat(stringWriter.toString(), equalTo(message));

如果你真的要做你正在做的事情,我所能建议的就是深入研究Mockito代码,看看是否有办法让它发生,并可能提交一份bug报告,看看他们对它的看法。

我目前正在用一个自定义的答案来解决这个问题

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 List writenchars=new ArrayList();
威尔回答(
新答案(){
@凌驾
公共对象应答(最终调用锁调用)抛出可丢弃{
final int arg=(int)invocation.getArguments()[0];
writenChars.add(arg);
返回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();
final迭代器writenCharitr=writenChars.Iterator();
for(int charInt:“Hello World!\n”.toCharArray())
断言(charInt,is(writenCharitr.next());
断言(“没有更多的字符了。”,writenCharitr.hasNext(),是(false));
验证(mockWriter.flush();
不过,如果您对不止一次的方法调用感兴趣,除非您在列表中记录调用了哪个方法,否则这将不起作用



编辑:向Brice致歉您似乎已经独立地找到了此解决方案,除非使用
StringBuilder
而不是
列表
,以独立和更好的方式,尽管对于一般情况,列表效果更好

我向之前的受访者道歉;但在我看来,使用答案有点违背了Mockito的一个基本思想,即存根和验证是两个完全独立的过程。Mockito具有存根功能和验证功能,Mockito的制造商努力将两者分开。答案是为了存根;虽然有一些情况下答案是最好的验证方式,但我不认为这是其中之一

我会用辩手而不是答案。我会在测试类中编写这样的方法,然后用“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());
}
private void verifyCharactersWrited(应为字符串){
ArgumentCaptor captor=ArgumentCaptor.forClass(Character.class);
验证(mockWriter,times(预期的.length()).write(captor.capture());
assertEquals(Arrays.asList(应为.toCharArray()),captor.getAllValues());
}

希望这能有所帮助。

Mockito这样工作的原因是顺序验证和常规验证之间的一致性。换句话说,如果我们不以这种方式实现它,API将以另一种方式令人惊讶:)您在尝试设计一个像样的API时做出了权衡

所以。。。答案是。首先,您应该避免在测试代码中使用循环(或条件)之类的语句。原因是您非常关心测试代码的清晰性和可维护性!=)

但是,如果我们从测试中删除循环,我们就不再有用例了。。。没有用例,很难给出答案。大卫的辩手可能不是个坏主意


希望有帮助

这是一个奇怪的测试,但仍然应该得到模拟API的支持。我相信Mockito可以支持它,因为其他mocking API也支持它

使用Unitils Mock:


无论何时使用inOrder
,并且相同的调用需要通过中间的其他调用(其他方法或具有其他参数的相同方法)进行验证,您都可以使用代替。

我正在编写一个虚拟机/解释器,因此I/O一次发生一个字符,没有完整消息的概念,只是打印到标准输出的一系列单个字符。我试图确保在打印过程中不会损坏任何东西(是的,我正在实现自己的内存)。我试图验证的消息是“Hello World”,因此我认为测试这样的消息是否可以正确地逐字打印并不过分嗨,如果这个测试很关键,你应该测试你正在做的事情,我同意@Ryan的观点,你应该更喜欢比较输入和输出。在存根编写器时,使用一个自定义答案,该答案将在通常的
StringBuilder
中附加字符,然后将其与我们的
输入“Hello World”
进行比较。或者,您也可以编写一个自定义匹配器;类似于
inOrder.verify(mockWriter,times(11)).write(charsthatmachinorder(“helloworld”),但是如果字符串更改,此测试可能很容易中断@很好,被模拟/解释的代码片段是测试输入的一部分,所以如果它发生变化,那么测试就会失败
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();
}
@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);
    }};
}