Почему Мокито дважды звонит моему помощнику? - PullRequest
8 голосов
/ 04 марта 2011

У меня есть тест Mockito, который выглядит примерно так (упрощенно, конечно):

@RunWith(MockitoJUnitRunner.class)
public class BlahTest {
    private static final int VERSION = 41;
    private static final int PAGE_SIZE = 4096;

    @Mock private FileChannel channel;

    @Test
    public void shouldWriteStandardHeader() throws Exception {
        final Blah blah = new Blah(channel, VERSION, PAGE_SIZE);
        blah.create();

        verify(channel).write(littleEndianByteBufferContaining(Blah.MAGIC_NUMBER,
                                                               VERSION,
                                                               PAGE_SIZE));
    }

    private ByteBuffer littleEndianByteBufferContaining(final int... ints) {
        return argThat(byteBufferMatcher(ints));
    }

    private Matcher<ByteBuffer> byteBufferMatcher(final int... ints) {
        return new TypeSafeMatcher<ByteBuffer>() {
            @Override
            public void describeTo(final Description description) {
                description.appendText("a little-endian byte buffer containing integers ").
                            appendValueList("", ",", "", ints);
            }

            @Override
            protected boolean matchesSafely(final ByteBuffer buffer) {
                if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
                    return false;
                }

                for (final int i : ints) {
                    if (buffer.getInt() != i) {
                        return false;
                    }
                }

                return true;
            }
        };
    }
}

По сути, этот тест пытается утверждать, что при вызове Blah.create() он записывает ByteBuffer, содержащий определенные данные, в FileChannel.

Когда я запускаю этот тест, средство вызова вызывается дважды. Это приводит к BufferUnderflowException.

Теперь я мог бы обойти это, просто сделав, чтобы matcher сохранил позицию буфера в начале вызова matchesSafely и переместил позицию обратно в нее в конце (в блоке finally), но мне кажется, что мой помощник не должен вызываться дважды.

Может кто-нибудь пролить свет на это?

РЕДАКТИРОВАТЬ # 1:

Вероятно, стоит отметить, что буфер перед переключением на канал переворачивается, поэтому позиция равна 0, а ограничение установлено на количество записанных данных.

Я отладил тест, и определитель вызывается дважды.

Я могу выполнить тестовый прогон, отметив буфер в начале matchesSafely() и сбросив его в конце, так что второй проход через механизм сопоставления считывает те же данные. Это также подтверждает, что matcher вызывается дважды, так как в противном случае он все равно потерпит неудачу.

РЕДАКТИРОВАТЬ # 2:

Похоже, что это ожидаемое поведение платформы Mockito. Оглядываясь назад, я могу сказать, что мой подход немного беден, потому что он изменяет глобальное состояние. Я изменил механизм сопоставления, чтобы записать начальную позицию и вернуться к ней в конце метода matchesSafely(). В любом случае, это, вероятно, хорошая идея, поскольку она сохраняет изменяющееся глобальное состояние. Я не использую mark() и reset() по той же причине.

Ответы [ 2 ]

5 голосов
/ 04 марта 2011

Я не думаю, что ваш сопоставитель вызывается дважды, вам просто нужно rewind ваш буфер перед чтением из него:

protected boolean matchesSafely(final ByteBuffer buffer) {
    if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
        return false;
    }
    buffer.rewind();
    ...
}

ОБНОВЛЕНИЕ

Итак, кажется, что на самом деле его вызывают дважды.В конце концов все это происходит методом verify.Если вы посмотрите на источники Mockito, существует метод Times.verify, который на самом деле проверяет 2 вещи:

  1. , если есть какие-либо пропущенные вызовы метода
  2. , что количество вызововметода является именно тем, что требуется

Mockito содержит список фактических вызовов всех методов для вашего channel фиктивного объекта.Чтобы проверить, какой из этих вызовов правильный, он сопоставляет каждый вызов с вашим сопоставителем.И это на самом деле делает это дважды.Пожалуйста, посмотрите на источники, чтобы получить полную идею.

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

3 голосов
/ 26 августа 2015

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

ArgumentCaptor<ByteBuffer> captor = ArgumentCaptor.forClass(ByteBuffer.class);
verify(channel).write(captor.capture());
assertThat(captor.getValue().order(), equalTo(ByteOrder.LITTLE_ENDIAN));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...