Mocks, Stubs, Fakes, and Test Doubles
Test Doubles are substitute objects that replace real dependencies during testing. They let you test components in isolation, remove external systems from the equation, and make tests deterministic. Understanding the differences between Mocks, Stubs, Fakes, Dummies, and Spies helps you write more precise and maintainable tests.
In a Nutshell
- Test Doubles replace real dependencies in tests.
- Dummy, Stub, Fake, Spy, and Mock each serve different purposes and exhibit different behavior.
- Mocks verify interactions; Stubs provide fixed responses; Fakes contain simplified logic.
- Too many Mocks can make tests brittle.
Quick Definition
The term “Test Double” comes from the stunt double in film. Just as a stunt performer replaces an actor, a Test Double assumes the role of a real dependency in a test. The goal is to isolate the unit under test from the outside world, so you evaluate only its behavior.
The Five Test Doubles
Dummy
A Dummy is an object that gets passed in but is never actually used. It exists only to satisfy a method’s signature.
Stub
A Stub returns predetermined responses to calls. It replaces a real dependency with a fixed result, without implementing any real logic.
Fake
A Fake contains a simplified but functional implementation. An in-memory repository is a classic example of a Fake.
Spy
A Spy records calls so they can be verified later. It’s essentially a hand-rolled Mock.
Mock
A Mock is an object where you define expected interactions before the test runs. After the test, you verify that those calls actually happened.
Practical Example: Order Processing with Test Doubles
from unittest.mock import Mock
class OrderService:
def __init__(self, payment_gateway, inventory_repository):
self.payment_gateway = payment_gateway
self.inventory_repository = inventory_repository
def process_order(self, order):
available = self.inventory_repository.is_available(order.product_id)
if not available:
return {"status": "failed", "reason": "out_of_stock"}
payment_result = self.payment_gateway.charge(order.amount)
if payment_result.success:
return {"status": "success"}
return {"status": "failed", "reason": payment_result.error}
class PaymentResult:
def __init__(self, success, error=None):
self.success = success
self.error = error
def test_order_success():
inventory = Mock()
inventory.is_available.return_value = True
payment = Mock()
payment.charge.return_value = PaymentResult(success=True)
service = OrderService(payment, inventory)
result = service.process_order(Mock(product_id=1, amount=99.99))
assert result["status"] == "success"
payment.charge.assert_called_once_with(99.99)
Advantages and Disadvantages
Advantages
- Isolation: External systems and slow dependencies are removed.
- Speed: Tests run without real databases or network calls.
- Determinism: Fixed return values make tests predictable.
- Focus: You verify only the behavior of the unit in question.
Disadvantages
- False confidence: Mocks can create an unrealistic picture of how things actually work.
- Maintenance burden: Tightly coupled Mocks break during refactoring.
- Overhead: Too many Test Doubles obscure the real behavior.
- Learning curve: The subtle differences between Mock and Stub are often misunderstood.
Key Exam Topics
- Definition and purpose of Test Doubles.
- Differences between Dummy, Stub, Fake, Spy, and Mock.
- When to use each type of Double.
- Advantages and risks of Mocks.
- Relationship to the test pyramid and isolated testing.
Common Exam Questions (with Brief Answers)
-
What is a Test Double? A substitute object for a real dependency in a test.
-
What’s the difference between a Mock and a Stub? A Stub provides fixed responses; a Mock verifies interactions.
-
What is a Fake? A substitute with simplified but functional logic.
-
When do you use a Dummy? When an object must be passed as a parameter but is never used.
-
What is a disadvantage of Mocks? They can couple too tightly to the implementation and break during refactoring.
Key Sources
- https://martinfowler.com/bliki/TestDouble.html
- https://xunitpatterns.com/Test%20Double.html
- https://en.wikipedia.org/wiki/Test_double
Frequently Asked Questions
What is the main reason for using Test Doubles?
Test Doubles replace real dependencies so you can test a component in isolation, quickly, and deterministically.
Is a Mock the same as a Stub?
No. A Stub provides predetermined responses, while a Mock defines expected interactions and verifies them afterward.
What is a typical example of a Fake?
An in-memory repository that replaces a real database but still performs basic storage and query operations.
When should you avoid using a Mock?
Avoid Mocks when the real interaction with a dependency is critical to the behavior you’re testing—for example, complex database queries.
What is a Spy?
A Spy records calls so you can verify them in your test. It’s a hand-rolled alternative to a Mock.
What is a Dummy?
A Dummy is an object passed in as a parameter but plays no role in the test itself.
Can Test Doubles hide bugs?
Yes, if they oversimplify or make false assumptions about how the real dependency behaves.
How many Mocks should a test contain at most?
There’s no hard rule, but a test with multiple Mocks quickly becomes complex and fragile. A clear focus and a few well-justified Doubles work better.
What is Interaction Testing?
Interaction Testing verifies that a component calls its dependencies in the expected way. Mocks are used for this.
What is State Testing?
State Testing checks the result or state after an action, without closely observing the interactions.
Should you mock external APIs?
In unit tests, yes—to achieve isolation and speed. For integration tests, prefer real or at least realistic interfaces.
What is an advantage of Fakes over Mocks?
Fakes contain real, though simplified, logic and give a more realistic picture of the dependency than rigid Mocks.
Which frameworks provide Test Doubles?
Common frameworks include Mockito for Java, unittest.mock for Python, Moq for C#, NSubstitute for .NET, and jest.fn() for JavaScript.
What does Over-Mocking mean?
Over-Mocking is when a test simulates too many dependencies. This makes the test fragile and less informative.
How do Test Doubles support the test pyramid?
They enable a large number of fast, isolated unit tests, forming the broad base of the test pyramid.
Continue Learning: Software Testing
The next article in the Software Testing learning path covers Test Driven Development: Red, Green, Refactor — the fundamentals of TDD and the Red-Green-Refactor cycle.



