Question
How can I mock methods that have a void return type using Mockito?
I implemented something like an observer pattern, but I am not sure how to test it correctly with Mockito.
I looked for examples online, but I could not find one that clearly matched my case.
My code is roughly like this:
public class World {
List<Listener> listeners;
void addListener(Listener item) {
listeners.add(item);
}
void doAction(Action goal, Object obj) {
setState("i received");
goal.doAction(obj);
setState("i finished");
}
private String state;
// getter and setter for state
}
public class WorldTest implements Listener {
@Test
public void word() {
World w = mock(World.class);
w.addListener(this);
// ...
}
}
interface Listener {
void doAction();
}
The expected behavior is not happening when World is mocked.
I want to observe the system state changes and make assertions based on them. How should I test or mock this properly with Mockito?
Short Answer
By the end of this page, you will understand how Mockito handles void methods, when to use doNothing(), doThrow(), and doAnswer(), and why fully mocking the class under test often prevents real behavior from running. You will also learn how to test state changes and interactions correctly in an observer-style design.
Concept
Mockito can mock void methods, but they work a little differently from methods that return a value.
For a normal method, you often write:
when(service.getName()).thenReturn("Alice");
That style does not work for void methods, because there is no return value to configure.
Instead, Mockito provides the do...when(...) syntax:
doNothing().when(mock).someVoidMethod();
doThrow(new RuntimeException()).when(mock).someVoidMethod();
doAnswer(invocation -> {
// custom behavior
return null;
}).when(mock).someVoidMethod();
Why this matters
In real code, void methods are common for:
- notifications
- logging
- sending events
- mutating object state
- updating other objects
Testing them usually means one of these:
- verifying that the method was called
- simulating side effects
- checking how other code reacts when the method throws an exception
The key issue in your example
The main problem is not just mocking a void method. It is that you are mocking World itself:
World w = mock(World.class);
When you fully mock World, its real methods do not run unless you explicitly tell Mockito to call them. That means:
addListener()does not really add anythingdoAction()does not really updatestatesetState("i received")andsetState("i finished")do not happen
So if you want to test the behavior of World, you usually should not mock World. Instead:
- create a real
Worldobject - mock or stub its collaborators, such as
ActionorListener - assert the resulting state or verify interactions
That is a core testing principle:
- mock dependencies
- do not mock the class you want to test
Mental Model
Think of a mock as a cardboard stand-in for a real object.
It looks like the object from the outside, but it does not do any real work unless you tell it exactly what to do.
So if you create:
World w = mock(World.class);
that mock is like an empty actor reading no lines. Calling:
w.doAction(...);
does not execute your real doAction() logic.
A better mental model is:
- Real object under test = the machine you want to inspect
- Mocks = fake buttons, sensors, or connected devices around it
If you want to see whether the machine changes state correctly, use the real machine. If you want to control or observe the things connected to it, mock those.
Syntax and Examples
With Mockito, void methods are configured using do...when(...).
Core syntax
doNothing().when(mock).voidMethod();
doThrow(new IllegalStateException()).when(mock).voidMethod();
doAnswer(invocation -> {
// custom side effect
return null;
}).when(mock).voidMethod();
Example 1: Verify that a void method was called
import static org.mockito.Mockito.*;
Listener listener = mock(Listener.class);
listener.doAction();
verify(listener).doAction();
This is the most common case. You do not always need to stub a void method at all. Often, you simply call the code and verify the interaction.
Example 2: Make a void method throw an exception
import static org.mockito.Mockito.*;
Listener listener = mock(Listener.class);
doThrow(new RuntimeException("failed")).when(listener).doAction();
listener.doAction(); // throws RuntimeException
This is useful for testing error handling.
Example 3: Custom behavior with doAnswer()
import static org.mockito.Mockito.*;
import java.util.concurrent.atomic.AtomicBoolean;
Listener listener = mock(Listener.class);
AtomicBoolean called = new AtomicBoolean(false);
doAnswer(invocation -> {
called.set(true);
return null;
}).when(listener).doAction();
listener.doAction();
System.out.println(called.get()); // true
Example 4: Testing a real class with mocked dependency
A better design for your scenario looks like this:
public interface Action {
void doAction(Object obj);
}
public class World {
private String state;
public void doAction(Action goal, Object obj) {
setState("i received");
goal.doAction(obj);
setState("i finished");
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Test:
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class WorldTest {
@Test
public void shouldUpdateStateBeforeAndAfterAction() {
World world = new World();
Action action = mock(Action.class);
doNothing().when(action).doAction("data");
world.doAction(action, "data");
verify(action).doAction("data");
assertEquals("i finished", world.getState());
}
}
Here:
Worldis realActionis mocked- the state change can be observed
- the void dependency call can be verified
Step by Step Execution
Consider this code:
public interface Action {
void doAction(Object obj);
}
public class World {
private String state;
public void doAction(Action goal, Object obj) {
setState("i received");
goal.doAction(obj);
setState("i finished");
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
And this test:
World world = new World();
Action action = mock(Action.class);
world.doAction(action, "file.txt");
What happens step by step
worldis a real object.actionis a mock.world.doAction(action, "file.txt")is called.- Inside
World.doAction(...),statebecomes"i received". - Then
goal.doAction(obj)runs. - Since
actionis a Mockito mock, the call is recorded. - By default, a mocked
voidmethod does nothing unless stubbed otherwise. - Execution continues.
statebecomes"i finished".- After the method finishes:
- you can verify that
action.doAction("file.txt")was called - you can assert that
world.getState()is"i finished"
- you can verify that
Trace example with assertion
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
World world = new World();
Action action = mock(Action.class);
world.doAction(action, "file.txt");
verify(action).doAction("file.txt");
assertEquals("i finished", world.getState());
If you mock World instead
World world = mock(World.class);
Action action = mock(Action.class);
world.doAction(action, "file.txt");
Now the real doAction() body does not run, so:
stateis never updatedaction.doAction("file.txt")is never called by real logic
That is why the expected behavior seems missing.
Real World Use Cases
void method mocking appears in many practical situations.
Event and observer systems
A listener often reacts through a void method:
listener.onMessage(message);
Tests usually verify that the listener was notified.
Logging and auditing
Services may call:
auditService.record(userAction);
The method returns nothing, but tests may verify it was called with the right data.
Email or notification sending
emailSender.sendWelcomeEmail(user);
You usually do not want to send a real email in a test, so you mock the sender and verify the call.
External side effects
Examples include:
- writing files
- publishing events
- sending metrics
- updating caches
These methods often return void, so mocks prevent real side effects while still letting tests check behavior.
Exception-path testing
A void dependency may fail:
doThrow(new RuntimeException("SMTP down")).when(emailSender).sendWelcomeEmail(user);
This helps test retry logic, fallback behavior, or error reporting.
Real Codebase Usage
In real projects, developers rarely mock everything. They usually test one unit of behavior at a time.
Common pattern: real class, mocked dependencies
OrderService service = new OrderService(paymentGateway, emailSender);
In a test:
OrderServiceis realpaymentGatewaymay be mockedemailSendermay be mocked
This lets the service logic run while dependencies stay controlled.
Verifying void interactions
Common in service tests:
verify(emailSender).sendWelcomeEmail(user);
verify(auditService).record("USER_CREATED");
Using doThrow() for error handling
Teams often test failure branches like this:
doThrow(new IOException()).when(fileWriter).write(data);
Then they check whether the application:
- logs the error
- retries
- returns a safe response
- throws a domain-specific exception
Guard clauses and validation
A method may stop before a void dependency is called:
if (user == null) {
return;
}
emailSender.sendWelcomeEmail(user);
Test:
verify(emailSender, never()).sendWelcomeEmail(any());
Capturing arguments
Sometimes you care about what was passed into a void method:
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(logger).info(captor.capture());
This is common for events, logging, and notifications.
Partial mocking with spies
If needed, developers sometimes use a spy:
World world = spy(new World());
A spy runs real code by default, but selected methods can be stubbed. This is useful when you want most real behavior but need to intercept one part. However, spies should be used carefully because they can make tests harder to understand.
Common Mistakes
1. Mocking the class you want to test
Broken approach:
World world = mock(World.class);
world.doAction(action, "data");
Why it fails:
- the real
doAction()method does not run - state is not updated
Better:
World world = new World();
Action action = mock(Action.class);
2. Using when(...).thenReturn(...) on a void method
Broken code:
when(listener.doAction()).thenReturn(...);
Why it fails:
voidmethods do not return a value
Correct approach:
doNothing().when(listener).doAction();
or simply verify the call without stubbing.
3. Expecting a mock to keep real state
Broken assumption:
World world = mock(World.class);
world.setState("hello");
assertEquals("hello", world.getState());
Why it fails:
Mocks do not behave like normal objects unless configured.
4. Forgetting to initialize collections
In your original class:
List<Listener> listeners;
If you call listeners.add(item) before initialization, you get a NullPointerException.
Fix:
private List<Listener> listeners = new ArrayList<>();
5. Testing state and interaction in the wrong place
If your goal is to check World state changes, test the real World.
If your goal is to check whether a listener or action was called, mock that collaborator.
6. Using a spy when a plain real object is enough
A spy is sometimes useful, but beginners often use it when a normal object would be simpler. Prefer the simplest test setup first.
Comparisons
| Approach | What it does | Good for | Common risk |
|---|---|---|---|
mock(Class) | Creates a fake object with no real behavior by default | Isolating dependencies | Real logic does not run |
spy(new Class()) | Wraps a real object and calls real methods by default | Partial mocking | Can make tests complex |
| Real object | Executes real code normally | Testing state changes and business logic | Harder if dependencies are external |
| Mockito syntax | Used for | Example |
|---|---|---|
when(...).thenReturn(...) | Non-void methods | when(repo.find()).thenReturn(user) |
doNothing().when(...) | Void methods | doNothing().when(sender).send() |
doThrow().when(...) | Void methods that should fail | doThrow(ex).when(sender).send() |
doAnswer().when(...) | Void methods with custom side effects | doAnswer(...).when(listener).doAction() |
verify vs stubbing for void methods
| Technique | Purpose |
|---|---|
verify(mock).method() | Check that a void method was called |
doNothing().when(mock).method() | Explicitly say it should do nothing |
doThrow(...).when(mock).method() | Simulate failure |
doAnswer(...).when(mock).method() | Simulate custom behavior or capture effects |
In many tests, verify(...) is enough and explicit doNothing() is not necessary.
Cheat Sheet
Quick rules
- Use
do...when(...)forvoidmethods - Use
when(...).thenReturn(...)for non-void methods - Do not mock the class you want to test unless you have a specific reason
- Mock collaborators, not the main behavior under test
Void method syntax
doNothing().when(mock).voidMethod();
doThrow(new RuntimeException()).when(mock).voidMethod();
doAnswer(invocation -> {
return null;
}).when(mock).voidMethod();
Verify calls
verify(mock).voidMethod();
verify(mock, never()).voidMethod();
verify(mock, times(2)).voidMethod();
Best practice for your scenario
World world = new World();
Action action = mock(Action.class);
world.doAction(action, "data");
verify(action).doAction("data");
assertEquals("i finished", world.getState());
Common edge cases
- Mocked objects do not keep real state
- Real methods do not run on a plain mock
voidmethods cannot usethenReturn(...)- Collections must still be initialized in real objects
Useful extras
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
verify(action).doAction(captor.capture());
doThrow(new IllegalStateException("boom")).when(action).doAction(any());
FAQ
How do you mock a void method in Mockito?
Use doNothing(), doThrow(), or doAnswer() with when(...), for example:
doThrow(new RuntimeException()).when(service).run();
Can I use when(...).thenReturn(...) with a void method?
No. That syntax only works for methods that return a value.
Why is my mocked object not changing state?
Because a Mockito mock does not run real code by default. If you want real state changes, use a real object or a spy.
Should I mock the class I am testing?
Usually no. Create a real instance of the class under test and mock its dependencies.
Do I need doNothing() for every void method?
No. Mockito does nothing by default for mocked void methods. Often you can skip stubbing and just use verify(...).
How do I test that a void method was called with the right argument?
Use verify(...) or ArgumentCaptor.
When should I use doAnswer()?
Use it when you want a mocked void method to perform custom logic, such as toggling a flag, capturing state, or simulating a side effect.
What is the difference between a mock and a spy?
A mock has no real behavior unless stubbed. A spy wraps a real object and calls real methods by default.
Mini Project
Description
Build a small Java test scenario where a World object updates its internal state before and after calling a dependency with a void method. This demonstrates the correct testing strategy: keep the main object real, mock the collaborator, and verify both state changes and interactions.
Goal
Create a test that confirms a void dependency is called and that the main object's state changes as expected.
Requirements
- Create a
Worldclass with astatefield and adoActionmethod. - Create an
Actioninterface with avoid doAction(Object obj)method. - Write a JUnit test that uses a real
Worldand a mockedAction. - Verify that the mocked
Actionis called with the expected argument. - Assert that the final
statevalue is correct after execution.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.