When you perform unit testing, there are many situations where you don’t have the full code or the right context that is needed to execute it. It might be that part of the code is not written yet or that you interact with an external system that is difficult to access or you don’t want to impact with unit tests. In these cases, you need to create a test double, an object that behaves like its production equivalent. In this article, Michał Lipski describes three types of test doubles – fake, stub and mock – and gives you examples when to use them in your unit testing.
Author: Michał Lipski, Pragmatists, http://pragmatists.pl/
In automated testing it is common to use objects that look and behave like their production equivalents, but are actually simplified. This reduces complexity, allows to verify code independently from the rest of the system and sometimes it is even necessary to execute self validating tests at all. A “test double” is a generic term used for these objects.
Although test doubles come in many flavors (Gerard Meszaros introduced five types in this article), people tend to use term “mock” to refer to different kinds of test doubles. Misunderstanding and mixing test doubles implementation may influence test design and increase the fragility of tests, standing on our way to seamless refactoring.
This article describes three implementation variations of testing doubles: fake, stub and mock and give you examples when to use them.
Fake
Fakes are objects that have working implementations, but not same as production one. Usually they take some shortcut and have simplified version of production code.
An example of this shortcut, can be an in-memory implementation of Data Access Object or Repository. This fake implementation will not engage a database, but will use a simple collection to store data. This allows us to do integration test of services without starting up a database and performing time consuming requests.
Example of Fake
@Profile("transient") public class FakeAccountRepository implements AccountRepository { Map<User, Account> accounts = new HashMap<>(); public FakeAccountRepository() { this.accounts.put(new User("john@bmail.com"), new UserAccount()); this.accounts.put(new User("boby@bmail.com"), new AdminAccount()); } String getPasswordHash(User user) { return accounts.get(user).getPasswordHash(); } }
Apart from testing, fake implementation can come handy for prototyping and spikes. You can quickly implement and run your system with in-memory store, deferring decisions about database design. A fake payment system that will always return successful payments can be another example.
Stub
A stub is an object that holds predefined data and uses it to answer calls during tests. It is used when you can’t or don’t want to involve objects that would answer with real data or have undesirable side effects.
An example can be an object that needs to grab some data from the database to respond to a method call. Instead of the real object, we introduced a stub and defined what data should be returned.
example of Stub
public class GradesService { private final Gradebook gradebook; public GradesService(Gradebook gradebook) { this.gradebook = gradebook; } Double averageGrades(Student student) { return average(gradebook.gradesFor(student)); } }
Instead of calling database from Gradebook store to get real students grades, you preconfigure stub with grades that will be returned. You define just enough data to test average calculation algorithm.
public class GradesServiceTest { private Student student; private Gradebook gradebook; @Before public void setUp() throws Exception { gradebook = mock(Gradebook.class); student = new Student(); } @Test public void calculates_grades_average_for_student() { when(gradebook.gradesFor(student)).thenReturn(grades(8, 6, 10)); //stubbing gradebook double averageGrades = new GradesService(gradebook).averageGrades(student); assertThat(averageGrades).isEqualTo(8.0); } }
Command Query Separation
Methods that return some result and do not change the state of the system, are called Query. Method averageGrades, that returns average of student grades is a good example.
Double averageGrades(Student student);
It returns a value and is free of side effects. As you have seen in students grading example, for testing this type of method we use stubs. You replace real functionality to provide values needed for method to perform its job. Then, values returned by the method can be used for assertions.
There is also another category of methods called Command. This is when a method performs some actions, that changes the system state, but you don’t expect any return value from it.
void sendReminderEmail(Student student);
A good practice is to divide an object’s methods into those two separated categories.
This practice was named: Command Query separation by Bertrand Meyer in his book “Object Oriented Software Construction”.
For testing Query type methods, you should prefer to use stubs as you can verify method’s return value. But what about Command type of methods, like method sending an e-mail? How to test them when they do not return any values? The answer is a mock – the last type of test dummy covered here.
Mock
Mocks are objects that register calls they receive. In test assertion you can verify on Mocks that all expected actions were performed. You use mocks when you don’t want to invoke production code or when there is no easy way to verify, that intended code was executed. There is no return value and no easy way to check system state change. An example can be a functionality that calls e-mail sending service.
You don’t want to send e-mails each time you run a test. Moreover, it is not easy to verify in tests that a right email was send. Only thing you can do is to verify the outputs of the functionality that is exercised in our test. In other worlds, verify that the e-mail sending service was called.
A similar case is presented in the following example:
Example of Mock
public class SecurityCentral { private final Window window; private final Door door; public SecurityCentral(Window window, Door door) { this.window = window; this.door = door; } void securityOn() { window.close(); door.close(); } }
You don’t want to close real doors to test that security method is working, right? Instead, you place door and window mocks objects in the test code.
public class SecurityCentralTest { Window windowMock = mock(Window.class); Door doorMock = mock(Door.class); @Test public void enabling_security_locks_windows_and_doors() { SecurityCentral securityCentral = new SecurityCentral(windowMock, doorMock); securityCentral.securityOn(); verify(doorMock).close(); verify(windowMock).close(); } }
After execution of securityOn method, window and door mocks recorded all interactions. This lets us verify that window and door objects were instructed to close themselves. That’s all you need to test from SecurityCental perspective. You may ask how can we tell if door and window will be closed for real if we use mock? The answer is that you can’t. But you don’t care about it. This is not responsibility of SecurityCentral. This is responsibility of Door and Window alone to close itself when they get proper signal. You can test it independently in different unit test.
Further Reading
Test Double – Martin Fowler https://martinfowler.com/bliki/TestDouble.html
Test Double – xUnit Patterns http://xunitpatterns.com/Test%20Double.html
Mocks Aren’t Stubs – Martin Fowler https://martinfowler.com/articles/mocksArentStubs.html
Command Query Separation – Martin Fowler https://martinfowler.com/bliki/CommandQuerySeparation.html
About the Author
Michał Lipski is a software developer at Pragmatists. He is a practitioner of Test Driven Development (TDD) and an enthusiast of lightweight technologies and Agile methodologies. He has experience working in cloud-based and startups projects. He is occasionally an Agile and XP practices trainer and conference speaker. This article was originally published on http://pragmatists.pl/blog/2017/03/test-doubles-fakes-mocks-or-stubs/ and is reproduced with permission from Pragmatists.