1. Overview
In this short tutorial, we focus on mocking void methods with Mockito.
Overview of Java 8 support in Mockito framework, including Streams and default interface methods
Learn to configure a method call to throw an exception in 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.
The code backing this article is available on GitHub. Once you're
logged in as a Baeldung Pro Member, start learning and coding on the project.