I've got a Mockito test that looks a bit like this (simplified, of course):
@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;
}
};
}
}
Essentially, this test is trying to assert that when Blah.create()
is invoked, it writes a ByteBuffer
containing certain data to the FileChannel
.
When I run this test, the matcher gets called twice. This results in a BufferUnderflowException
.
Now, I could get around this by just having the matcher store the buffer position at the beginning of the matchesSafely
call and move the position back to that at the end (in a finally block), but it seems to me that my matcher shouldn't be being called twice.
Can anyone shed any light on this?
EDIT #1:
It's probably worth noting that the buffer is flipped before being passed to the channel, so the position is 0 and the limit is set to the amount of data written.
I've debugged the test, and the matcher is definitely getting called twice.
I can make the test pass by marking the buffer at the beginning of matchesSafely()
and resetting it at the end, so the second pass through the matcher reads the same data. This also confirms that the matcher is getting called twice, as otherwise it would still fail.
EDIT #2:
So it looks like this is expected behaviour of the Mockito framework. In retrospect, my matcher is a bit poor because it modifies global state. I have modified the matcher to record the starting position and seek back to it at the end of the matchesSafely()
method. This is probably a good idea anyway since it saves modifying global state. I don't use mark()
and reset()
for the same reason.
I don't think that your matcher gets called twice, you just have to rewind
your buffer before reading from it:
protected boolean matchesSafely(final ByteBuffer buffer) {
if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
return false;
}
buffer.rewind();
...
}
UPDATE
So, appears it actually does get called twice. It eventually it all happens in verify
method. If you take a look at Mockito sources there is Times.verify
method which is actually verifies 2 things:
Mockito holds a list of actual invocations of all methods on your channel
mock object. To verify which of these invocations are correct it matches every invocation with your matcher. And it actually does it twice. Please take a look at the sources to get the whole idea.
I'm not sure if it's a bug or not, you should ask Mockito devs. I suggest it won't hurt to rewind
your buffer in the matchesSafely
method every time to fix the problem - it should not hurt the correctness.
It would be better to use ArgumentCaptor
to verify argument to avoid custom matcher invoked twice.
ArgumentCaptor<ByteBuffer> captor = ArgumentCaptor.forClass(ByteBuffer.class);
verify(channel).write(captor.capture());
assertThat(captor.getValue().order(), equalTo(ByteOrder.LITTLE_ENDIAN));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With