1. Overview
In this short tutorial, we focus on mocking void methods with Mockito.
Further reading:
Mockito Support for Optional, Streams, Lambda Expressions
Mocking Exception Throwing using 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
}
}
2. Simple Mocking and Verifying
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.
3. Argument Capture
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());
}
4. Answering a Call to Void
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().
5. Calling the Original void Method
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.
5.1. Using doCallRealMethod()
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.
5.2. Mock vs. Spy
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.
6. Conclusion
In this brief article, we covered four different ways to approach void methods when testing with Mockito.
