When testing Java applications, we often encounter void methods — methods that don’t return anything. Examples include logging, sending notifications, or deleting records. Unlike regular methods, we can’t use when(...).thenReturn(...)
for void methods. Instead, Mockito provides special stubbing methods: doNothing()
and doThrow()
.
This tutorial will guide you through stubbing void methods in Mockito using real-world examples, best practices, and practical code snippets.
Why Stub Void Methods?
- Avoid Side Effects: Prevent methods like
delete()
from modifying data during tests. - Simulate Failures: Throw exceptions to test error handling logic.
- Ensure Reliability: Make tests consistent and deterministic.
- Control Behavior: Focus on testing business logic, not external effects.
Think of stubbing void methods as rehearsing a play without the props: you simulate the action without real-world consequences.
Example: Notification Service
Class Under Test
class NotificationService {
void sendEmail(String user, String message) {
// Actual implementation calls an external SMTP server
System.out.println("Email sent to " + user);
}
}
class UserService {
private final NotificationService notificationService;
UserService(NotificationService notificationService) {
this.notificationService = notificationService;
}
void registerUser(String user) {
// Business logic...
notificationService.sendEmail(user, "Welcome!");
}
}
Using doNothing()
doNothing()
tells Mockito to ignore the method call.
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
class UserServiceTest {
@Test
void shouldRegisterUserWithoutSendingEmail() {
NotificationService mockNotification = mock(NotificationService.class);
doNothing().when(mockNotification).sendEmail(anyString(), anyString());
UserService userService = new UserService(mockNotification);
userService.registerUser("alice@example.com");
verify(mockNotification).sendEmail("alice@example.com", "Welcome!");
}
}
Here, the test runs without sending a real email.
Using doThrow()
doThrow()
simulates exceptions from void methods.
@Test
void shouldHandleEmailFailure() {
NotificationService mockNotification = mock(NotificationService.class);
doThrow(new RuntimeException("SMTP error"))
.when(mockNotification).sendEmail(anyString(), anyString());
UserService userService = new UserService(mockNotification);
try {
userService.registerUser("bob@example.com");
} catch (RuntimeException e) {
assert e.getMessage().equals("SMTP error");
}
}
This helps verify that your code handles failures gracefully.
Combining doNothing() and doThrow()
Mockito allows chaining behaviors for sequential calls.
doNothing()
.doThrow(new RuntimeException("SMTP down"))
.when(mockNotification).sendEmail(anyString(), anyString());
- First call → does nothing.
- Second call → throws exception.
Best Practices
- Use
doNothing()
to suppress unwanted side effects (logs, deletes, network). - Use
doThrow()
to simulate failures and test error handling. - Don’t overuse void stubs — test only meaningful behavior.
- Combine with
verify()
to ensure the void method was called. - Keep error messages descriptive for clarity.
Mockito + Testcontainers
Mockito stubs dependencies, while Testcontainers provides real integration testing.
Example: Use Mockito to stub email sending, but Testcontainers to test database persistence:
import org.testcontainers.containers.PostgreSQLContainer;
@Test
void shouldStartDatabase() {
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
postgres.start();
assert postgres.isRunning();
}
}
Together, they provide both speed (mocks) and reliability (containers).
Version Tracker
- JUnit 4 → JUnit 5: Improved integration with Mockito extensions.
- Mockito Updates: Support for mocking static, final, and constructor methods.
- Testcontainers Growth: Expanded modules for integration alongside mocks.
Conclusion & Key Takeaways
Stubbing void methods is essential for handling side effects and exceptions in unit tests. Mockito’s doNothing()
and doThrow()
provide clean and flexible ways to control void method behavior.
Key Takeaways:
- Use
doNothing()
to neutralize void methods. - Use
doThrow()
to test failure handling. - Verify void interactions with
verify()
. - Balance mocks with integration tests using Testcontainers.
FAQ
1. Why can’t I use when().thenReturn() for void methods?
Because void methods don’t return anything — use doNothing()
or doThrow()
instead.
2. Can I suppress logging in tests with doNothing()?
Yes, you can stub logging frameworks or notification services.
3. How do I test exception handling in void methods?
Use doThrow()
to simulate errors.
4. What’s the difference between doNothing() and verify()?doNothing()
stubs behavior; verify()
checks if a method was called.
5. Can I chain doNothing() and doThrow()?
Yes, for sequential behavior simulation.
6. Should I stub all void methods?
No, only those with side effects or external dependencies.
7. Does Mockito support final void methods?
Yes, with inline mocking in newer versions.
8. Can I use argument matchers with void stubs?
Yes, any()
and eq()
work with doNothing()
and doThrow()
.
9. Do mocks improve test speed?
Yes, by removing slow I/O and network calls.
10. Should I migrate to JUnit 5 for Mockito?
Yes, JUnit 5 offers cleaner integration and modern features.