
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: September 7, 2024
In this short tutorial, we focus on mocking void methods with Mockito.
As with other articles focused on the Mockito framework (such as Mockito Verify, Mockito When/Then, and Mockito’s Mock Methods), the MyList class shown below will be used as the collaborator in test cases.
We’ll add a new method for this tutorial:
public class MyList extends AbstractList<String> {
@Override
public void add(int index, String element) {
// no-op
}
}
Void methods can be used with Mockito’s doNothing(), doThrow(), and doAnswer() methods, making mocking and verifying intuitive:
@Test
public void whenAddCalled_thenVerified() {
MyList myList = mock(MyList.class);
doNothing().when(myList).add(isA(Integer.class), isA(String.class));
myList.add(0, "");
verify(myList, times(1)).add(0, "");
}
However, doNothing() is Mockito’s default behavior for void methods.
This version of whenAddCalledVerified() accomplishes the same thing as the one above:
@Test
void whenAddCalled_thenVerified() {
MyList myList = mock(MyList.class);
myList.add(0, "");
verify(myList, times(1)).add(0, "");
}
doThrow() generates an exception:
@Test
void givenNull_whenAddCalled_thenThrowsException() {
MyList myList = mock(MyList.class);
assertThrows(Exception.class, () -> {
doThrow().when(myList).add(isA(Integer.class), isNull());
});
myList.add(0, null);
}
We’ll cover doAnswer() below.
One reason to override the default behavior with doNothing() is to capture arguments.
In the example above, we used the verify() method to check the arguments passed to add().
However, we may need to capture the arguments and do something more with them.
In these cases, we use doNothing() just as we did above, but with an ArgumentCaptor:
@Test
void givenArgumentCaptor_whenAddCalled_thenValueCaptured() {
MyList myList = mock(MyList.class);
ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);
doNothing().when(myList).add(any(Integer.class), valueCapture.capture());
myList.add(0, "captured");
assertEquals("captured", valueCapture.getValue());
}
A method may perform more complex behavior than merely adding or setting value.
For these situations, we can use Mockito’s Answer to add the behavior we need:
@Test
void givenDoAnswer_whenAddCalled_thenAnswered() {
MyList myList = mock(MyList.class);
doAnswer(invocation -> {
Object arg0 = invocation.getArgument(0);
Object arg1 = invocation.getArgument(1);
assertEquals(3, arg0);
assertEquals("answer me", arg1);
return null;
}).when(myList).add(any(Integer.class), any(String.class));
myList.add(3, "answer me");
}
As explained in Mockito’s Java 8 Features, we use a lambda with Answer to define custom behavior for add().
In some cases, although we mocked an object, we would like to call an original void method during our testing. This is also called partial mocking.
Let’s see an example:
class Greeting {
private static final Logger LOG = LoggerFactory.getLogger(Greeting.class);
public void sayHello(String name) {
LOG.info("Hi {}, how are you?", name);
}
}
As the code shows, the Greeting class has a void method sayHello(). Next, let’s see how to call this method through a mock.
As the name implies, Mockito’s doCallRealMethod() can be used for calling the original void methods:
@Test
void whenDoCallRealMethodOnMock_thenRealMethodCalled() {
Greeting greeting = mock(Greeting.class);
doCallRealMethod().when(greeting).sayHello(any(String.class));
greeting.sayHello("Tom");
verify(greeting, times(1)).sayHello("Tom");
}
If we run the test, it passes with the output:
14:39:21.111 [main] INFO com.baeldung...Greeting -- Hi Tom, how are you?
Therefore, we call the original void method using doCallRealMethod() through a mock and verify() it.
Sometimes, we might get unexpected results when we leverage doCallRealMethod() to invoke the original void method through a mock object. Let’s see an example:
class Greeting {
private static final Logger LOG = //... unchanged code
private Instant timestamp = Instant.now();
public void sayHello(String name) //... unchanged code
public void sayHelloWithTs(String name) {
LOG.info("Hi {}, how are you? [Instance Created at {}]", name, timestamp);
}
}
As the code shows, we add a new void method, sayHelloWithTs(), to the Greeting class. This method’s implementation references an instance variable, timestamp, which records the object creation time.
Now, let’s call this method through a mock:
@Test
void whenDoCallRealMethodWithPropertyOnMock_thenRealMethodCalled() {
Greeting greeting = mock(Greeting.class);
doCallRealMethod().when(greeting).sayHelloWithTs(any(String.class));
greeting.sayHelloWithTs("Jerry");
verify(greeting, times(1)).sayHelloWithTs("Jerry");
}
The test passes, so sayHelloWithTs() has been invoked once. Let’s examine the output:
21:33:32.464 [main] INFO com.baeldung...Greeting -- Hi Jerry, how are you? [Instance Created at null]
The log output shows the creation timestamp as null, although we know it won’t be null. This is because when Mockito creates a mock object using mock(), it doesn’t initialize instance variables. A mock object isn’t a real instance of the class. Instead, it is a proxy or dummy object that simulates the class’s behavior under test without actually invoking its internal logic.
To solve the problem, we can call the void method through a spy instead of a mock since spy() copies the instance state over to create a more realistic interaction:
@Test
void whenDoCallRealMethodOnSpy_thenGetExpectedResult() {
Greeting greeting = spy(Greeting.class);
doCallRealMethod().when(greeting).sayHello(any(String.class));
greeting.sayHello("Tom");
verify(greeting, times(1)).sayHello("Tom");
doCallRealMethod().when(greeting).sayHelloWithTs(any(String.class));
greeting.sayHelloWithTs("Jerry");
verify(greeting, times(1)).sayHelloWithTs("Jerry");
}
In this example, we create a spy on Greeting using spy() and call the two void methods via the spy. Let’s see the output we get after executing this test:
21:52:26.516 [main] INFO com.baeldung...Greeting -- Hi Tom, how are you?
21:52:26.521 [main] INFO com.baeldung...Greeting -- Hi Jerry, how are you? [Instance Created at 2024-09-24T19:52:26.498859Z]
So, we get the expected result.
In this brief article, we covered four different ways to approach void methods when testing with Mockito.