Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a Mockito mock perform different actions in sequence?

Tags:

java

mockito

The following code:

  ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
  Mockito.doThrow(new IOException()).when(mapper).writeValue((OutputStream) Matchers.anyObject(), Matchers.anyObject());
  Mockito.doNothing().when(mapper).writeValue((OutputStream) Matchers.anyObject(), Matchers.anyObject());

  try {
      mapper.writeValue(new ByteArrayOutputStream(), new Object());
  } catch (Exception e) {
      System.out.println("EXCEPTION");
  }

  try {
      mapper.writeValue(new ByteArrayOutputStream(), new Object());
  } catch (Exception e) {
      System.out.println("EXCEPTION");
  }

The expected output is

EXCEPTION

right?

But I get nothing

If I then make the doThrow after the doNothing I get

EXCEPTION
EXCEPTION

So It looks like it is the last mock that is the one that is taken... I thought it would take the mocks in the order they're registered?

I'm looking to produce a mock throws the exception first time and completes normally the second time...

like image 543
Michael Wiles Avatar asked Nov 20 '13 14:11

Michael Wiles


1 Answers

Mockito can stub consecutive behavior with the same parameters—forever repeating the final instruction—but they all have to be part of the same "chain". Otherwise Mockito effectively thinks you've changed your mind and overwrites your previously-mocked behavior, which isn't a bad feature if you've established good defaults in a setUp or @Before method and want to override them in a specific test case.

The general rules for "which Mockito action will happen next": The most-recently-defined chain that matches all arguments will be selected. Within the chain, each action will happen once (counting multiple thenReturn values if given like thenReturn(1, 2, 3)), and then the last action will be repeated forever.

// doVerb syntax, for void methods and some spies
Mockito.doThrow(new IOException())
    .doNothing()
    .when(mapper).writeValue(
        (OutputStream) Matchers.anyObject(), Matchers.anyObject());

This is the equivalent of chained thenVerb statements in the more-common when syntax, which you correctly avoided here for your void method:

// when/thenVerb syntax, to mock methods with return values
when(mapper.writeValue(
        (OutputStream) Matchers.anyObject(), Matchers.anyObject())
    .thenThrow(new IOException())
    .thenReturn(someValue);

Note that you can use static imports for Mockito.doThrow and Matchers.*, and switch to any(OutputStream.class) instead of (OutputStream) anyObject(), and wind up with this:

// doVerb syntax with static imports
doThrow(new IOException())
    .doNothing()
    .when(mapper).writeValue(any(OutputStream.class), anyObject());

See Mockito's documentation for Stubber for a full list of commands you can chain.

like image 69
Jeff Bowman Avatar answered Oct 20 '22 11:10

Jeff Bowman