Test Doubles
TL;DR
- Test Double is the generic, academic term for any fake object used in testing (like a stunt double in movies).
- The five main types of Test Doubles are: Dummy, Stub, Spy, Mock, and Fake.
- Mockito can generate Mocks, Spies, and Stubs.
Concept
Often developers use the word “Mock” to refer to any fake object. Technically, this is incorrect. Martin Fowler and Gerard Meszaros categorized test doubles into 5 distinct types based on their behavior:
- Dummy: Objects passed around just to satisfy compilers/constructors, but never actually used or called in the test (e.g., passing
nullor an empty string). - Stub: An object that provides hardcoded answers to calls made during the test (e.g.,
when(repo.get()).thenReturn(User)). It does not care how many times it was called. - Spy: A stub that also records information about how it was called (e.g., counting how many times an email was sent).
- Mock: An object pre-programmed with expectations. It will explicitly fail the test if it is not called exactly as expected (e.g.,
verify(service, times(1)).execute()). - Fake: A working implementation, but one that takes a shortcut making it unsuitable for production (e.g., an In-Memory Database like H2 instead of PostgreSQL).
Examples
// 1. DUMMY
// We need a Logger to construct the service, but the test doesn't care about logging.
Logger dummyLogger = new EmptyLogger();
UserService service = new UserService(dummyLogger);
// 2. STUB (Using Mockito)
// We just need it to return true so we can test the next line of code.
UserRepository stubRepo = mock(UserRepository.class);
when(stubRepo.userExists(1)).thenReturn(true);
// 3. FAKE
// A real, working implementation of a repository, but stored in a HashMap in RAM.
public class FakeUserRepository implements UserRepository {
private Map<Integer, User> db = new HashMap<>();
public void save(User u) { db.put(u.getId(), u); } // Actually works!
public User get(int id) { return db.get(id); }
}
Interview Questions
Q: What is the difference between a Stub and a Mock?
A: A Stub focuses on State Verification. You stub a method to return data, run your test, and check if the final output of your method is correct.
A Mock focuses on Behavior Verification. You use a mock to verify that your code actually interacted with a dependency correctly (e.g., “Did my code call the sendEmail() method exactly once with these exact parameters?”).
Q: Is an In-Memory H2 Database a Mock?
A: No, it is a Fake. A Fake actually has working business logic (it parses SQL, maintains tables, and returns data). A Mock has no internal logic; it only returns exactly what you hardcoded it to return. Fakes are mostly used in Integration Testing, while Mocks and Stubs are used in Unit Testing.