Bert's Blog

Posts tagged mockito

Jul 12

Mockito Magic

I like unit tests, for one simple reason. I don’t trust myself! Tell me your name and I’ll forget it. Give me something to look after, and I’ll lose it. Put something down for a minute and I’ll step on it. 

Of course I’m exaggerating, I’m not that bad … or am I?

Tell me your name, and I’ll probably use a mnemonic to remember it. Give me something to look after and I’ll include it in my three-pocket-pat. Put something down and I’ll demand that you pick it up immediately or make some excuse to leave. I use unit tests in the same way, to avoid tripping over my own feet.

Anyway, I’d like to share a really neat feature of Mockito. Consider the following contrived example:

public class Example {
  private static final Logger LOG = Logger.getLogger("");
  public void cleanUp(InputStream is) {
    try {
      is.close();
    } catch(IOException e) {
      LOG.log(Level.FINEST,"IOE handled",e);
    } catch(NullPointerException e) {
      LOG.log(Level.WARNING,"NPE handled",e);
    }
  }
}

If you really wanted it to be bullet proof, you’d probably write a unit test like this:

public class ExampleTest {
  private final Example example = new Example();

  @Test
  public void testCleanUpInputStream() 
  throws IOException {
    InputStream is = mock(InputStream.class);
    example.cleanUp(is);
    verify(is).close();
  }

  @Test
  public void testCleanUpNullPointerException() 
  throws IOException {
    InputStream is = mock(InputStream.class);
    NullPointerException e = new NullPointerException();
    doThrow(e).when(is).close();
    example.cleanUp(is);
    verify(is).close();
  }

  @Test
  public void testCleanUpIOException() 
  throws IOException {
    InputStream is = mock(InputStream.class);
    IOException e = new IOException();
    doThrow(e).when(is).close();
    example.cleanUp(is);
    verify(is).close();
  }
}

It’s a good attempt, but there is a snag. There is nothing to verify that there was any real difference between testCleanUpNPE() and testCleanUpIOE(). But how to test it, after all Logger is static so doesn’t that make verifying behaviour problematic? The answer requires a minor refactor to create a new log() method:

public class Example {
  private static final Logger LOG = Logger.getLogger("");
  public void cleanUp(InputStream is){
    try {
      is.close();
    } catch(IOException e) {
      log(Level.FINEST,"IOE handled",e);
    } catch(NullPointerException e) {
      log(Level.WARNING,"NPE handled",e);
    }
  }
  protected void log(Level l, String m, Throwable e){
    LOG.log(l,m,e);
  }
}

And now enter Mockito.spy(). Similar to mock(), this returns an object stub of your original object, where all calls still invoke the same methods, only it’s also a mock. Now you can verify the behaviour as you would do with a mock. For example:

public class ExampleTest {
  private Example example = new Example();

  @Test
  public void testCleanUpInputStream() 
  throws IOException {
    Example spyExample = spy(example);
    InputStream is = mock(InputStream.class);
    spyExample.cleanUp(is);
    verify(is).close();
    verify(spyExample,never()).log(any(Level.class),anyString(),any(Throwable.class));
  }

  @Test
  public void testCleanUpNullPointerException() 
  throws IOException {
    Example spyExample = spy(example);
    InputStream is = mock(InputStream.class);
    NullPointerException e = new NullPointerException();
    doThrow(e).when(is).close();
    spyExample.cleanUp(is);
    verify(is).close();
    verify(spyExample).log(Level.WARNING,"NPE handled",e);
  }

  @Test
  public void testCleanUpIOException() 
  throws IOException {
    Example spyExample = spy(example);
    InputStream is = mock(InputStream.class);
    IOException e = new IOException();
    doThrow(e).when(is).close();
    spyExample.cleanUp(is);
    verify(is).close();
    verify(spyExample).log(Level.FINEST,"IOE handled",e);
  }
}

Mockito is awesome, and spy() is a very powerful tool as it allows you to verify interactions within the class itself. Because spy() essentially returns a mock, you can do “Partial Mocking”. However this I will save for another time. Now where did I put my keys?