Purpose
- Mocking simplifies unit testing by replacing the dependencies of the unit being tested with simplified, simulated versions called mocks.
- Example: Consider a unit under test that relies on a database. In testing, the database can be mocked to return a static value, eliminating the need for an actual database.
Benefits of Mocking
- Isolation of units to test each unit separately, dramatically reducing complexity and increasing test execution speed by replacing loaded modules with mocks.
- Simplifies the re-creation of specific scenarios (use cases, boundary cases).
- Expose hidden internals of production code without compromising encapsulation.
- Injection of test-specific behaviors not present in production code.
- Enables the simulation of indirect dependencies by letting mocks return other mocks.
Types of Mocks
Stubs are by far the most common type of mock. Keep your tests as simple as possible. Make them more complex only when necessary.
- Stubs: Simplest form, returning a hardcoded value or providing an empty method body.
- Fake object: Include minimal logic to handle different case scenarios.
- Spy: Injected to capture interaction data with fake objects when such data is not directly accessible.
- Mock objects: Contain complex logic, simulate behaviors such as computation and exception handling, and even run tests.
Tips
- Mock third-party libraries in unit tests to ensure proper unit functionality. Instead, use these libraries in component and integration tests.
- Aim for a minimal number of dependencies in a unit for easier testing and mocking:
- Limit dependencies in a unit in a similar way to the best practices for function arguments: the fewer the better, with an absolute maximum of three.
- Prefer many small classes/units to one large one for easier testing.
- If a class has excessive dependencies, consider splitting it up or extracting some dependencies into a new class to create smaller, more cohesive units.
- If a production class requires more than one test class, it's probably a sign that the class is too large.
- Overly complex test code may indicate an overly large production class.