Why did Mokito call me twice?

I have 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 attempts to claim that when Blah.create() is ByteBuffer , it writes a ByteBuffer containing certain data to the FileChannel .

When I run this test, the caller is called twice. The result is a BufferUnderflowException .

Now I could get around this by simply matching the buffer position at the beginning of the matchesSafely call and moving the position back to the end (in the finally block), but it seems to me that my interlocutor should not call twice.

Can anyone shed some light on this?

EDIT # 1:

It may be worth noting that the buffer turned upside down before being transmitted to the channel, so the position is 0, and the limit is set to the amount of recorded data.

I debugged the test, and the ripener definitely gets the call twice.

I can make a test pass by marking the buffer at the beginning of matchesSafely() and matchesSafely() it at the end, so that the second pass through the controller reads the same data. This also confirms that pairing is called twice, because otherwise it will still fail.

EDIT No. 2:

So it looks like this is the expected behavior of the Mockito structure. Looking back, my assistant is a little poor because he is changing the global state. I changed the matches to record the starting position and return to it at the end of the matchesSafely() method. This is probably a good idea, as it preserves a change in global status. I do not use mark() and reset() for the same reason.

+6
mockito
source share
2 answers

I do not think that your interlocutor is called twice, you just need 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 it seems that it actually calls the call twice. Ultimately, all this happens in the verify method. If you look at the sources of Mockito, there is a Times.verify method that actually checks 2 things:

  • if there are any missing method calls
  • that the number of method calls is exactly what is required

Mockito contains a list of actual calls to all methods of your channel layout. To check which of these links is correct, it matches each call with your agreement. And it actually does it twice. Please take a look at the sources to get the whole idea.

I'm not sure if this is a mistake or not, you should ask the Mockito developers. I suggest that every time I fix the problem, rewind your buffer in the matchesSafely method does not matchesSafely - this should not damage the correctness.

+4
source share

It would be better to use an ArgumentCaptor to test the argument to avoid an arbitrary call called twice.

 ArgumentCaptor<ByteBuffer> captor = ArgumentCaptor.forClass(ByteBuffer.class); verify(channel).write(captor.capture()); assertThat(captor.getValue().order(), equalTo(ByteOrder.LITTLE_ENDIAN)); 
+3
source share

All Articles