A mock is a test double that stands in for a real dependency and records how the code under test interacts with it, so that a test can verify the expected calls, arguments and call counts. In software testing, mocks isolate a unit from databases, network services, clocks and other collaborators that are slow, unpredictable or expensive to use.
The term is often used loosely for any fake object, but in the precise vocabulary of test doubles a mock is defined by behavior verification: the test fails if the interaction with the mock does not match what was specified. Frameworks such as Mockito for Java, unittest.mock for Python, Jest for JavaScript and Moq for .NET make creating mocks a routine part of unit testing.
How a mock works in a test
A mock replaces a collaborator through dependency injection, configuration or runtime patching. The test first programs the mock with the answers it should give, then exercises the code under test, and finally asserts on the recorded interactions, for example that a payment gateway was called exactly once with an amount of 120.
This produces the classic arrange, act, assert structure. In the arrange step, the mock is created and its return values or exceptions are configured. In the act step, the unit runs normally and talks to the mock as if it were the real dependency. In the assert step, the test checks both the result of the unit and the calls recorded by the mock.
Mocking frameworks generate these objects automatically, usually through dynamic proxies or runtime code generation, so the developer does not write a fake class by hand. Some frameworks support strict mocks, which fail on any unexpected call, and lenient mocks, which return default values for calls that were not configured.
Mock compared with other test doubles
The widely used taxonomy of test doubles distinguishes five kinds that differ in what they provide and what the test checks. Choosing the right kind keeps tests simple and robust.
| Double | What it does | What the test verifies |
|---|---|---|
| Dummy | Fills a parameter and is never used | Nothing about the dummy |
| Stub | Returns predefined answers | The state or result of the unit |
| Spy | A stub that also records calls | Calls, checked after the fact |
| Mock | Preprogrammed with expected calls | That the expected interactions happened |
| Fake | A working lightweight implementation | The state or result of the unit |
The essential contrast is between state verification and behavior verification. A stub supports state verification: the test checks what the unit returned or stored. A mock supports behavior verification: the test checks what the unit asked its collaborators to do. An in-memory database is a fake, not a mock, because it really stores and retrieves data.
Where mocks fit among test levels
Mocks belong mainly to unit tests, where the goal is to examine one unit in isolation and get feedback in milliseconds. As tests move toward integration and end-to-end levels, real components replace mocks step by step.
| Test level | Typical use of mocks | Main benefit | Main risk |
|---|---|---|---|
| Unit test | Mock most external collaborators | Speed and precise failure location | Tests coupled to implementation |
| Integration test | Mock only third-party systems | Real wiring between own components | Slower setup |
| End-to-end test | Few or no mocks | Confidence in the whole system | Fragile and slow |
| Contract test | Mock the provider from a shared contract | Detects API drift early | Contract must be maintained |
In test-driven development, mocks help design interfaces from the outside in. The so-called London school of TDD uses mocks heavily to specify how objects collaborate, while the classic or Detroit school prefers real objects and mocks only at system boundaries. Both approaches are legitimate, and the choice influences how test coverage numbers should be interpreted.
Deciding what to mock
A sound rule is to mock what you do not own or cannot control, and use real objects for simple values and in-process logic. The following decision table summarizes common cases.
| Dependency | Recommended double | Reason |
|---|---|---|
| Payment or email service | Mock | Side effects must be verified, not executed |
| System clock or random source | Stub | Makes results deterministic |
| Database repository | Fake or stub | State checks are clearer than call checks |
| Value object or data class | Real object | Mocking adds noise and no isolation |
| Third-party library type | Wrap it, then mock the wrapper | Avoids coupling to an API you do not own |
Mocks are most valuable when the interaction itself is the requirement: an order service must publish exactly one event, or a retry policy must call the server three times before giving up. When the requirement is a computed value, a stub or a fake usually produces a clearer test.
Pitfalls of overusing mocks
The main risk of mocks is coupling tests to implementation details. A test that specifies every internal call breaks whenever the code is refactored, even if its observable behavior has not changed, which discourages improvement.
- Over-specification: verifying calls that do not matter to the requirement makes tests brittle.
- Mocks that drift from reality: a mock may accept arguments the real service rejects, so tests pass while production fails.
- Mocking the unit under test: partial mocks of the same class hide real defects.
- Deep mock chains: mocks that return mocks signal a design that violates the principle of least knowledge.
- False confidence: high unit coverage with heavy mocking does not prove the components work together.
The usual remedy is a balanced test suite: mocks for fast unit feedback, a smaller number of integration tests with real components, and contract tests or a few end-to-end tests to confirm that the mocked assumptions match the real systems.
The simple explanation
A mock is a stand-in that pretends to be a real part of a program during a test. It answers questions the way the real part would, and it also writes down every request it receives.
Imagine rehearsing a restaurant order with a friend who plays the waiter. The friend does not cook anything, but afterward can tell you exactly what you ordered and how many times you asked for water. A mock lets programmers check that their code asks for the right things, without contacting a real bank, server or email system.
How it is used in practice
A developer builds a password reset feature for a web application. When a user requests a reset, the code must create a token, store it and send exactly one email containing a link. Sending real emails in every test run would be slow, could reach real inboxes and would depend on an external server.
In the unit test, the email service is replaced by a mock. The test calls the reset function for a sample address and then verifies that the mock received one send call with that address and a message containing the token. A second test configures the mock to throw a connection error and confirms that the feature reports a friendly failure instead of crashing.
The whole test class runs in under 100 milliseconds. A separate integration test, run nightly, uses a local mail catcher to confirm that the real email client formats messages correctly, which covers the gap that the mock cannot.