Type of Mocks

A long time ago (year 2000), the mock objects were invented. It is now one of the most important parts of unit testing.

For those you don’t know, the idea of a mock object is to simulate a dependency to easily test a class. Quick example.

I have a class Pricer.

public class Pricer {
  private PriceFeed priceFeed;
  
  public BigDecimal getPrice(String symbol) {
    BigDecimal latest = priceFeed.getLatestPrice(symbol);
    // ... do some calculations ...
    return value;
  }
}

Being a nice human being, I want to test my calculations but I don’t want to use a real PriceFeed. The real implementation has to an actual MQSeries queue that received prices from Reuters. It’s not something you want to do during your unit tests (if at all).

So you will instead do a mock object, an object that will behave as you see fit for your test but that isn’t a real PriceFeed. It just mimics it.

Not so long ago, Uncle Bob did a blog post about mock objects. He classifies them into five different types or levels. Levels because each type is wiser than the previous one. He calls them “Test Doubles”.

I’ve decided to show you how to code them. Using EasyMock (of course), Mockito and by hand.

Type of mocks

From the most basic to the most advanced type.

Dummy

A class that you pass into something when you don’t care how it’s used. e.g. As part of a test, when you must pass an argument, but you know the argument will never be used.

public class DummyAuthorizer implements Authorizer {
  public Boolean authorize(String username, String password) {
    return null;
  }
}

In this EasyMock world, it is called a nice mock (Authorized mock = niceMock(Authorized.class)).

In the Mockito world, it’s just a mock (Authorized mock = mock(Authorized.class)).

In general, if a dummy is used, you will want it to throw an exception to tell you something is wrong. So, with Mockito, you will in general get a NullPointerException (or not) if you do something like

if(mock.authorize(user, password)) // NPE

With EasyMock, you will generally prefer to use a normal mock (Authorized mock = mock(Authorized.class)) that will make sure nothing unintended is called.

if(mock.authorize(user, password)) // AssertionError: Unexpected method call

Stub

A class that returns a valid answer but always the same one.

public class AcceptingAuthorizerStub implements Authorizer {
  public Boolean authorize(String username, String password) {
    return true;
  }
}

In the EasyMock language, this is any mock with an expectation recorded (expect(mock.authorize(anyString(), anyString()).andStubReturn(true)).

In the Mockito language, this is a mock with behavior set on a method (when(mock.authorize(any(), any()).thenReturn(true).

Spy

You use a spy when you want to be sure that the authorize() method is called by the system.

public class AcceptingAuthorizerSpy implements Authorizer {
  public boolean authorizeWasCalled = false;

  public Boolean authorize(String username, String password) {
    authorizeWasCalled = true;
    return true;
  }
}

In EasyMock, it means you are not stubbing anymore. You want to record a precise call (expect(mock.authorize(anyString(), anyString()).andReturn(true)) and then verify that the call actually occurred (verify(mock)).

In Mockito, you still stub the call and then verify the call occurred (verify(authorizer).authorize(any(), any())). Note that Mockito has its own concept of a spy, which is different. A Mockito spy is a shell over an actual class that allows to verify calls to them. It is indeed useful but it isn’t a mock. So don’t get lost in the semantic.

True Mock

A true mock is a mock that knows how to verify itself. In fact, EasyMock and Mockito mocks are always true mocks. So their implementations of a true mock is the same as for the spy.

public class AcceptingAuthorizerVerificationMock implements Authorizer {
  public boolean authorizeWasCalled = false;

  public Boolean authorize(String username, String password) {
    authorizeWasCalled = true;
    return true;
  }

  public boolean verify() {
    return authorizedWasCalled;
  }
}

Fake

A Fake has business behavior. You can drive a fake to behave in different ways by giving it different data. They are usually used for integration testing to simulate other parts of your system.

public class AcceptingAuthorizerFake implements Authorizer {
  public Boolean authorize(String username, String password) {
        return "Bob".equals(username);
      }
}

I rarely use a mocking framework for them. It tends to make the code more complicated than coding by hand. Still, a mocking framework can be used.

// Easymock
expect(authorizer.authorize(anyString(), anyString())).andStubAnswer(() -> "Bob".equals(getCurrentArguments()[0]));

// Mockito
when(authorizer.authorize(any(), any())).thenAnswer(invocationOnMock -> "Bob".equals(invocationOnMock.getArgument(0)));

Conclusion

When jumping from one flavor to another, you should make sure you really need to. Because the more complicated your mocking is, the more coupling you will have with the actual implementation. It makes the test more fragile. But you still need to make sure everything is working as expected!

Testing advice

Modify to test

If your code isn’t easy to test, modify your code. Do whatever is needed. You will end up with a better design anyway. A test should not be complicated. If it needs to, something is wrong.

Provide a testing framework

If you build something, you should provide a nice framework to test it. Spring has spring-test. You are responsible for making what you do testable, mockable, etc.

Use as less mocks as possible

Usually, unit tests should use at worst 3 mocks. It you have more, you probably should split your code in smaller parts. A lot of mocks makes the code unreadable.

Explain and document your tests

Tests are harder to understand than actual production code. When someone reads test code, he should understand the purpose. So use a nice test name to explain what you wanted to do. Use javadoc. Use line comments to explain the flow.

Refactor them

I’m refactoring my tests a lot. A lot. Nice methods preventing copy & paste. Testing frameworks. Fixtures. Base test classes. Everything to make it as pretty as my production code.