In the article How to Write Testable Code, I touched on the concept of Mocks. So what is a Mock? And how can you leverage it to make Unit Testing significantly simpler and more reliable?

What is a Mock?
A Mock is a simulated test double whose behavior is pre-programmed dynamically at runtime to mimic the responses of real collaborator classes during test execution. Furthermore, mocks allow you to verify whether specific methods were invoked, with what parameters, and how many times.
Sounds a bit abstract?
Consider this practical test scenario:
Test a method that queries customers with total purchases exceeding $3,000 from a Database.
Without mocking, unit testing this introduces serious hurdles:
- Must the unit test connect to a live database? What happens if test data changes or the database becomes unreachable during CI runs?
- How can you verify that the repository method was called with parameter 3,000 rather than 2,999? If the query is executed twice accidentally inside a loop, returning identical data, how would you catch that bug without verifying invocations?
This is where Mocking comes to the rescue!
Beyond databases, Mocks are invaluable when collaborating with:
- Non-deterministic suppliers (Random numbers, current system timestamps, weather feeds, external clocks…).
- External infrastructure (Databases, REST/gRPC endpoints, message queues, caching layers…).
- Slow or resource-heavy computations.
- Components not yet implemented by other teammates. For example, in Custom Exception, we can unit test SalaryTransfer ahead of time without waiting for a teammate to finish TransferMoney by
Mocking the return values :muscle:. - Context-dependent state (Thread context, security context, session scope…).
- …
Note: For an in-depth exploration of test double taxonomy, read Martin Fowler’s Mocks Aren’t Stubs.
Mock and Dependency Injection
In traditional tightly coupled designs, business classes instantiate their own data access objects, making database isolation difficult. With IoC and DI (see Inversion of Control and Dependency Injection), classes receive dependencies via injection. During runtime, real implementations are supplied; during unit testing, mock objects are effortlessly injected instead.
Example Unit Test with Mock
Given the following requirement:
Display the full names of customers whose total purchases exceed N from the Database, where N is provided as an input.
We define CustomerRepository:
| |
And map the retrieved Customer entities to full names in CustomerService:
| |
Setup MockK in our test class:
| |
And here is a representative test case:
| |
A standard mock-based test case follows 3 distinct phases (Arrange, Act/Assert, Verify):
Setup (Arrange) (lines 5-9): Configure the mock stub (
every { ... } returns ...) specifying what valuecustomerRepository.findByTotalBuyPriceGreaterThan(3_000)should return when called.Assert (Act & Assert) (lines 11-14): Execute the unit under test and compare actual results against expected values.
Verify (line 16): Ensure the mock collaborator was actually invoked with the expected parameters and exact call count (
verify(exactly = 1)). Skipping verification is risky because it fails to catch accidental duplicate calls—imagine accidentally executing multiple duplicate salary transfers as in Clean Code with Exception :sweat:!
Note 1: Here we use Spring Data JPA, which dynamically provisions repository implementations. In other architectures, write concrete classes and let Spring’s DI container inject them at runtime.
Note 2: The full sample code is updated in this repo.
Conclusion
With the power of Mocks, unit testing becomes much cleaner, faster, and reliable.
Thanks for reading!