9

次のような 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ます。

このテストを実行すると、マッチャーが 2 回呼び出されます。これにより、BufferUnderflowException.

これで、呼び出しの開始時にマッチャーにバッファー位置を保存matchesSafelyさせ、最後に (finally ブロックで) その位置に戻すことでこれを回避できましたが、私のマッチャーはそうすべきではないように思えます。 2回呼び出されます。

誰でもこれに光を当てることができますか?

編集#1:

チャネルに渡される前にバッファーが反転されることに注意してください。そのため、位置は 0 であり、制限は書き込まれたデータの量に設定されます。

テストをデバッグしましたが、マッチャーは間違いなく 2 回呼び出されています。

バッファーを最初にマークし、最後にリセットすることでテストに合格できるmatchesSafely()ので、マッチャーの 2 回目のパスで同じデータが読み取られます。これにより、マッチャーが 2 回呼び出されていることも確認できます。

編集#2:

したがって、これは Mockito フレームワークの予想される動作のようです。振り返ってみると、グローバル状態を変更するため、私のマッチャーは少し貧弱です。マッチャーを変更して、開始位置を記録し、matchesSafely()メソッドの最後でそこに戻るようにしました。グローバル状態の変更を保存するため、これはおそらく良い考えです。私は同じ理由でmark()andを使用しません。reset()

4

2 に答える 2

5

あなたのマッチャーが2回呼び出されるとは思わないrewind.バッファから読み取る前にバッファにアクセスする必要がある.

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

アップデート

したがって、実際には 2 回呼び出されるようです。最終的にはすべてverifyメソッドで発生します。Mockito のソースを見ると、Times.verify実際に 2 つのことを検証するメソッドがあります。

  1. メソッドの呼び出しが欠落している場合
  2. メソッドの呼び出し回数が正確に必要な数であること

Mockito は、channelモック オブジェクトのすべてのメソッドの実際の呼び出しのリストを保持します。これらの呼び出しのどれが正しいかを検証するために、すべての呼び出しをマッチャーと照合します。そして、実際には2回実行します。ソースを見て全体像を把握してください。

バグかどうかはわかりませんが、Mockito の開発者に問い合わせてください。問題を修正するたびrewindに、メソッド内のバッファに影響を与えないことをお勧めします-正確性を損なうべきではありません。matchesSafely

于 2011-03-04T00:32:10.313 に答える
4

ArgumentCaptorカスタムマッチャーが 2 回呼び出されるのを避けるために、引数を検証するために使用することをお勧めします。

ArgumentCaptor<ByteBuffer> captor = ArgumentCaptor.forClass(ByteBuffer.class);
verify(channel).write(captor.capture());
assertThat(captor.getValue().order(), equalTo(ByteOrder.LITTLE_ENDIAN));
于 2015-08-25T23:14:57.210 に答える