When to use Fakes instead of Mocks

Christian Dehning
3 min readJul 7, 2017

--

When unit testing Android apps, I usually make a lot of use of Mockito. Basically, I mock away every object that is outside my class I am currently testing. It helps me focusing on the behavior of this one class I want to test.

However, there are some cases in which I switched from using Mocks to using so called Fake objects instead. And generally those cases are all related to those objects maintaining some state.

But first, what is the difference between a Mock and a Fake?

A Mock object just replays trained behavior. Usually you would create them with a library like Mockito at the beginning of your test case. Then, you tell that Mock what it should respond with, whenever a certain method is called throughout the test. No actual implementation of the class you mocked will be called (unless you are using Mockito and your methods are static of final… ). It does not even matter if you mock an Interface which is not even implemented yet.

In comparison, for a Fake you need to do some manual work. If you are in Java, you should start by making sure that your class has an Interface. And then, you can just create an anonymous implementation of that Interface within your test.

So, manual setup for testing is annoying because it takes time and is error prone. Why should we do that? Because we can add a simple state to that fake object.

Consider you need to test something like the following:

You have a class doing (something overly simplified like) this:

class SomethingToTest {

private Repository<String> stringRepository;

public SomethingToTest() {
stringRepository = new RemoteStringRepository();
}

public SomethingToTest(Repository<String> repository) {
this.stringRepository = repository;
}

public String saveAndLoad(String string) {
stringRepository.save(string);
return stringRepository.load();
}

}

While the RemoteStringRepository is something stateful, for example like that:

class RemoteStringRepository implements Repository<String> {

@Override
public void save(String object) {
// Make a POST call to some API
}

@Override
public String load() {
// Make a GET call to some API
return "the response";
}
}

Then writing a unit test with Mocks is a bit tricky. Something like the following will just fail:

@Test
public void testSaveAndLoad_WithMock() {
// Given
Repository<String> mockRepository = Mockito.mock(Repository.class);
testee = new SomethingToTest(mockRepository);

String toSave = "some string";

// When
String loaded = testee.saveAndLoad(toSave);

// Then
assertEquals
(toSave, loaded); // loaded will be null, the test will fail
}

It fails, simply because you did not tell your mocked object that it should return something when the load() method is called. You would need to mock that behavior.

In order to do so, you would need to replicate code happening in your stateful class within this test (aka you leave your black box and have no more unit test).

In some cases you can use verifying and ArgumentCaptor to solve this problem with Mockito. This way, you can extract whatever arguments your methods were called with. And then you could mock another method to return exactly those object you extracted.

But wait, have a look at how straight forward this is with having a Fake instead.

First, you need an interface for your stateful object:

interface Repository<T> {

void save(T object);

T load();

}

Then, you can write your test like that:

public class ExampleUnitTest {

private Repository<String> fakeRepository = new Repository<String>() {
public String object;

@Override
public void save(String object) {
this.object = object;
}

@Override
public String load() {
return object;
}
};

private SomethingToTest testee = new SomethingToTest(fakeRepository);

@Test
public void testSaveAndLoad_WithFake() {
// Given
String toSave = "some string";

// When
String loaded = testee.saveAndLoad(toSave);

// Then
assertEquals
(toSave, loaded);
}
}

Of course, Fakes are way less flexible than Mocks. For example, if you want to test some error behavior (“Do I react right, when my save() call throws an Exception?”), then you need create a different Fake in those test cases.

However, testing state changes gets really simple. And if your stateful code usually is also more complicated than the above example(greetings to all app developers reading this), then you should probably give it a try as well.

--

--