Mocking strategies for effective tests: when to mock and when not to
Mocks are one of the most misused testing tools. Used correctly, they isolate your tests and make them fast. Used incorrectly, they create brittle tests that test implementation details rather than behavior.
Mock external boundaries, not internal details. Mock your database, external API, file system, and network calls. These are the boundaries between your code and the outside world. Mocking these gives you fast, deterministic tests. Never mock internal classes or functions.
Fake objects are often better than mocks. A fake is a lightweight implementation of an external interface that works in memory. An in-memory database, a file system backed by a dictionary, or a fake email sender that stores messages in a list. Fakes are more robust than mocks.
Set up mocks at the API boundary, not deep in the code. If you mock at a low level, a refactoring that changes the internal implementation will break the mock without changing the behavior. Mock at the highest abstraction level possible.
Assert on behavior, not on implementation. Instead of verifying that a specific method was called with specific arguments, verify that the system produced the expected outcome. Assert that the email was queued, not that the email service's send method was called.
Mock as little as possible. Every mock makes your test less realistic. Prefer real implementations when they're fast and reliable. Use in-memory databases, test containers, and fake services before resorting to mocks.
Keep your mock setup close to the test. Don't share mock configurations across tests changes to shared mocks can break unrelated tests. Each test should set up its own expectations. This makes the test self-documenting.
-
Rizwan Saleem | https://rizwansaleem.co
Top comments (0)