18 · Testing the complete backend / LESSON 89 OF 102
Mockito, fakes, and dependency boundaries
A test double replaces an external collaboration so the test can control its behavior.
Before this lesson: JUnit lifecycle and parameterized tests
Understand the topic
Use mocks for interactions that matter to the contract and simple fakes for in-memory behavior. Do not mock the class under test or every value object. Excessive verification of private implementation steps makes harmless refactoring break tests.
Stub both success and failure paths. Verify absence of a write when validation fails, because that side effect is part of the contract. Keep integration tests for the seams that mocks cannot validate, such as SQL mappings and serialization.
EXAMPLE · JAVA
NotificationTest.java
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
class NotificationTest {
interface Sender { void send(String message); }
record Reminder(Sender sender) { void remind() { sender.send("Review Java"); } }
@Test void sendsTheRequiredReminder() {
Sender sender = mock(Sender.class);
new Reminder(sender).remind();
verify(sender).send("Review Java");
}
}What to expect
The interaction is the required output of this small service. This test does not prove email delivery or external provider behavior.
YOUR TURN
Put it into practice
- 01Test a service using a repository double.
- 02Make the repository fail and assert the service does not emit a success notification.
- 03Verify: The failure-path test catches a false success and does not require a live database.
Pass a quiz or complete a lab to check today’s streak. Each lesson counts once per day, including when you review it later.
UNDER THE HOOD
Build the mental model
A test double replaces an external collaboration so the test can control its behavior.
- 01Use mocks for interactions that matter to the contract and simple fakes for in-memory behavior. Do not mock the class under test or every value object. Excessive verification of private implementation steps makes harmless refactoring break tests.
- 02Stub both success and failure paths. Verify absence of a write when validation fails, because that side effect is part of the contract. Keep integration tests for the seams that mocks cannot validate, such as SQL mappings and serialization.
- 03Trace the example: The interaction is the required output of this small service. This test does not prove email delivery or external provider behavior.
A mistake worth avoiding
A service test stays green after the real SQL query is broken.
DEBUGGING ROOM
Something broke. Where do you look?
A service test stays green after the real SQL query is broken.
Before revealing the diagnosis, write down your first investigation step.
CHECK YOUR UNDERSTANDING
Can a mocked repository test prove its SQL is valid?
BUILD IT YOURSELF
Test a service using a repository double.
- 1.Test a service using a repository double.
- 2.Make the repository fail and assert the service does not emit a success notification.
- 3.Reproduce this failure, then repair it: A service test stays green after the real SQL query is broken.
Acceptance checklist
Lab completion is your self-report. Knowledge checks do not certify project quality.
RETRIEVE IT FROM MEMORY
Can a mocked repository test prove its SQL is valid?
Revisit this lesson in three days after a successful check or lab.
Ready for the next step?
The failure-path test catches a false success and does not require a live database.
Reading is separate from passing a knowledge check or completing a lab.