Mocking replaces a real dependency, a payment gateway, an email service, a database client, with a substitute object that records how your code calls it, so the test can later verify that the call happened the way it should have. That verification step is what makes a mock a mock rather than some other kind of test double: you are not just checking what your code returned, you are checking how it behaved toward something else. That distinction, checking behavior instead of state, is both the entire value of mocking and the source of most of its problems.
What changed in 2026
- Mocking frameworks leaned further into type safety. Statically typed languages increasingly generate mocks from interfaces automatically, catching a mock that no longer matches its real counterpart at compile time instead of at test runtime.
- AI-assisted test generation made over-mocking easier to produce at scale, since an assistant asked to test a function will often mock every collaborator by default; reviewing generated tests for mocks that assert nothing meaningful became a necessary step, not an optional one.
- Contract testing grew as a partner practice, catching what mocking cannot: whether your mock of a service still matches what that service actually does today, not what it did when you wrote the mock.
Behavior verification vs state verification
This is the distinction that separates mocking from its close relatives.
|
State verification (stub / fake) |
Behavior verification (mock) |
| What you check |
The output your code produces |
Whether your code called the dependency correctly |
| Example assertion |
expect(result).toBe(42) |
expect(paymentGateway.charge).toHaveBeenCalledWith(amount) |
| Fails when |
The output is wrong |
The interaction did not happen as expected, even if the output looks fine |
| Coupling risk |
Low |
Higher, ties the test to how the code happens to call its dependency |
For a full taxonomy of test doubles beyond just mocks, see what a test double actually is.
How mocking frameworks work
Writing a mock by hand means creating a class that implements the same interface as the real dependency, records every call it receives, and lets the test assert on that record afterward. A mocking framework, Mockito for Java, Jest or Sinon for JavaScript, unittest.mock for Python, generates that class at runtime or compile time so you only write the interesting part: which calls you expect, what they should return, and how many times they should happen.
When to mock
- External systems you do not control — payment providers, third-party APIs, email and SMS providers. You cannot and should not call the real thing in a test.
- Slow or non-deterministic collaborators — anything that would make the test slow or occasionally fail for reasons unrelated to your code.
- Verifying that a side effect happened — like confirming an audit log was written, when the side effect itself has no return value to check.
When mocking backfires
- Mocking your own domain logic. If you own the class and it is fast and deterministic, use the real thing. A mock of your own code tests that your code calls your code, which is not useful.
- Mocking a type you do not own directly. A third-party SDK can change its interface on you; mocking it directly means every test breaks or, worse, silently stops matching reality. Wrap it in your own interface and mock that instead.
- Asserting call order and arguments no one actually cares about. Over-specified mocks fail on harmless refactors, which trains a team to stop trusting test failures.
FAQ
Is mocking the same as stubbing?
No. A stub returns canned data and is not itself verified; the test only checks the eventual output. A mock is verified directly, the test asserts the mock was called in a specific way.
Do mocking frameworks slow down tests?
No, the opposite. A mock avoids the real dependency entirely, which is usually the slowest part of a test. Mocking frameworks add negligible overhead compared to what they replace.
What does over-mocking actually cost a team?
Confidence in test failures. Once enough tests fail on harmless refactors because they were checking implementation details through mocks, people start ignoring red tests, which defeats the entire point of having them.
Should I mock the database in a unit test?
Generally yes, for a true unit test, replace it with a mock, fake, or in-memory version. Save real-database testing for a separate integration test suite that runs less frequently.
Where to go next