Как проверить в Mockito конкретный порядок вызовов с одинаковыми аргументами? - PullRequest
5 голосов
/ 04 декабря 2011

Я хочу проверить конкретный порядок, в котором персонажи должны убедиться, что они не искажены. Я пытался написать его, используя 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)

Как написать для этого тест Мокито?


РЕДАКТИРОВАТЬ: Подано как ошибка http://code.google.com/p/mockito/issues/detail?id=296

Ответы [ 5 ]

19 голосов
/ 05 декабря 2011

Мои извинения предыдущим респондентам;но, по моему мнению, использование ответа несколько противоречит одной из основных идей Mockito, а именно, что проверка и проверка - это два совершенно разных процесса.У Mockito есть функции для заглушки и функции для проверки, и создатели Mockito приложили усилия, чтобы разделить их.Ответы предназначены для окурков;и хотя есть несколько случаев, когда ответ является лучшим способом проверки, я не верю, что это один из них.

Я бы использовал ArgumentCaptor вместо ответа.Я бы написал такой метод в тестовом классе, а затем вызвал бы его с «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());
}

Надеюсь, это поможет.

4 голосов
/ 04 декабря 2011

Проверка по порядку является отдельным понятием от того, сколько раз что-то делается, поэтому, когда вы добираетесь до 'l' и говорите Mockito, чтобы убедиться, что это произошло, оно проходит проверку по порядку, но не проходит, потому что 'l Вызов был сделан три раза, и вы (неявно) сказали ему ожидать его только один раз. Это причуды, которые я использовал ранее в Mockito, но почти каждый раз, когда это происходит, я решаю, что мой тест написан плохо, и когда я его исправляю, проблема исчезает. В вашем случае, я бы сказал, что способ излишне проверять каждый символ, написанный для Writer. Если вы хотите убедиться, что сообщение было отправлено правильно, вы должны сравнить входное сообщение с выходным сообщением. В вашем примере это может повлечь за собой использование StringWriter вместо насмешки над писателем. Тогда конец вашего теста выглядит как

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

Если вам действительно нужно делать то, что вы делаете, все, что я могу предложить, - это покопаться в коде Mockito, чтобы посмотреть, есть ли способ сделать это, и, возможно, подать отчет об ошибке, чтобы узнать, что они говорят об этом.

2 голосов
/ 06 декабря 2011

Причина, по которой Mockito работает подобным образом, заключается в согласованности между проверкой по порядку и регулярной проверкой. Другими словами, если бы мы не реализовали это таким образом, API был бы удивительным по-другому :) Вы делаете компромиссы, сделанные при попытке разработать достойный API.

Итак ... ответ. Во-первых, вы должны избегать таких выражений, как циклы (или условные выражения) в тестовом коде. Причина в том, что вы очень заботитесь о четкости и удобстве сопровождения тестового кода! =)

Если мы удалим циклы из теста, у нас больше не будет варианта использования, хотя ... Без варианта использования трудно дать ответ. ArgumentCaptor Дэвида не может быть плохой идеей.

Надеюсь, это поможет!

0 голосов
/ 07 декабря 2011

Это странный тест, но, тем не менее, он должен поддерживаться насмешливым 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);
    }};
}
0 голосов
/ 04 декабря 2011

В настоящее время я пытаюсь решить эту проблему с помощью пользовательского ответа.

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();

Хотя это не сработает, если вы заинтересованы в вызове метода более одного раза, если только вы не запишите в списке, какой метод был вызван и т. Д.


РЕДАКТИРОВАТЬ: извинения перед Брайсом, вы, кажется, независимо пришли к этому решению, кроме как независимо и лучше, используя StringBuilder вместо List, хотя для общего случая список работает лучше.

...