Quata 4: Mocking in Unit Tests

feature image

Quata 4: Mocking in Unit Tests

In Quata 3, I presented one half of my definition of “correct” when it comes to unit tests. In this quata, I will present the other half.

Consider the snippet below, which tests an ExchangeRateClient’s getRate() method.

@Before
public void setUp() {
  // code that launches a local HTTP server listening on port 8080
  // and returns canned responses.  How it's implemented is irrelevant.
}

@Test
public void extractsExchangeRateFromResponse() {
  SomeHttpClient httpClient = new SomeHttpClient("localhost", 8080);
  assertEquals(1.5, new ExchangeRateClient(httpClient).getRate("ABC", "DEF"), 0.001);
}

@Test(expected = RuntimeException.class)
public void handlesBadResponse() {
  SomeHttpClient httpClient = new SomeHttpClient("localhost", 8080);
  new ExchangeRateClient(httpClient) {
    @Override
    // getRate() calls extractExchangeRate()
    protected double extractExchangeRate(String httpResponse) {
      throw new RuntimeException();
    }
  }.getRate("ABC", "DEF");
}

The first test, the happy path, relies on actual HTTP server accepting requests and returning responses. Sure, it sets up its own server, but it has two problems:

  • Port 8080 could already be taken.
  • It’s slow. Unit tests are supposed to be lightning fast. You don’t want your builds to run long.

Let’s back up a little bit. SomeHttpClient is a dependency. You do not need to and should not test it itself here (which the above test does when it sends a request to the HTTP server). It’s the perfect candidate for mocking. You can program it however way you want to simulate the different scenarios that you want to subject ExchangeRateClient to.

In general, you should assume that external dependencies are doing their job. Feel free to mock them all away.


The second test goes the other direction. It mocks a method of the code under test. It passes because it is controlling its own outcome. It’s expected to throw a RuntimeException and it does, but only because you made it throw it. I’ve seen this kind of thing done a few times in the wild.

Again, the solution here is to back up towards SomeHttpClient. It is what you want to mock. Make it return a bad response and let ExchangeRateClient parse it.


Here’s the corrected version. It assumes the use of Mockito. You can however use your mocking framework of choice, including your own subclasses if you’re a DIY mocker.

The two tests are mocking SomeHttpClient, exactly as I prescribed above.

The code plugin I’m using is incorrectly displaying ampersands encoded. Sorry about that. I will fix it later when I get the chance.

@Before
public void setUp() {
  // no need to set up local HTTP server
}

@Test
public void extractsExchangeRateFromResponse() {
  SomeHttpClient httpClient = mock(SomeHttpClient.class);
  when(httpClient.get("from=ABC&to=DEF")).thenReturn("{\"rate\" : 1.5}");
  assertEquals(1.5, new ExchangeRateClient(httpClient).getRate("ABC", "DEF"), 0.001);
}

@Test(expected = RuntimeException.class)
public void handlesBadResponse() {
  SomeHttpClient httpClient = mock(SomeHttpClient.class);
  when(httpClient.get("from=ABC&to=DEF")).thenReturn("someBadResponse");
  new ExchangeRateClient(httpClient).getRate("ABC", "DEF");
}

Okay, here it is. The other half of my definition of “correct”:

A unit test is correct if it mocks everything that needs to be mocked, no more, no less.

The key is to find that sweet spot. And that sweet spot is usually the external dependencies.

In the real world, I actually often find myself overriding methods of the class I’m testing. It’s usually when the method I’m currently testing calls a bunch of other methods that have their own branches. If you test all combinations of paths through the calling method, you’d end up with tons of tests, unnecessarily at that. So instead of doing that, I would test the calling method with the other methods mocked out. Then, I test the other methods individually.

Under-mocking, as the first test originally did, leads to a likely-unnecessary dependence on external behavior, including system functions such as I/Os. When you find yourself writing a lot of set-up code, like the HTTP server setup code above (which I didn’t bother fleshing out), or relying on the presence of test resources, such as an actual test input file, you’re probably under-mocking. Ask yourself if you could mock the reading of that test input file instead of actually reading one.

Over-mocking, as the second test originally did, usually indicates confusion on the part of the developer. As you have seen above, it usually leads to the test testing itself.

Unit testing is certainly an acquired skill. I’ve seen many people struggle with it, the concept and all the tricky stuff. I plan to cover more unit-testing topics in the future, and hopefully discover the secret to making the brain grasp it more naturally.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post navigation