only() vs. times(1) in Mockito

2021 Dec 13

Use times(1) to verify a method is called exactly once.

  verify(orderService, times(1)).getOrder(anyLong());
  verify(orderService, times(1)).createOrder(any(CreateOrderRequest.class));

The above code verifies that both getOrder and createOrder are called exactly once on orderService.

If the times(1) in above example is replaced by only(), the test would fail.

As per Mockito Javadoc of only(),

Allows checking if given method was the only one invoked

verify(mock, only()).someMethod(); is same as

  verify(mock).someMethod();
  verifyNoMoreInteractions(mock);

i.e.

  verify(mock, times(1)).someMethod();
  verifyNoMoreInteractions(mock);

Therefore,

  verify(orderService, only()).getOrder(anyLong());
  verify(orderService, only()).createOrder(any(CreateOrderRequest.class));

is equivalent to

  verify(orderService, times(1)).getOrder(anyLong()); // L1
  verifyNoMoreInteractions(orderService); // L2
  verify(orderService, times(1)).createOrder(any(CreateOrderRequest.class)); // L3
  verifyNoMoreInteractions(orderService); // L4

It would fail at line “L2”.

Why @Mock Annotated Object Is Null

2021 May 19

Mockito provides annotations like @Mock, @Spy, @Captor to make code simpler. Since most of our code is just copied from Google search and Stack Overflow, we may use these Mockito annotations like below, after quick reading of Google search results like this article.

@RunWith(MockitoJUnitRunner.class)
class OrderTest {
    @Captor
    ArgumentCaptor<Offer> offerCaptor;

    @Test
    void test() {
      // use the `offerCaptor` object ...
    }

However, when run, the test throws NullPointerException due to the offerCaptor is null, even though it’s annotated by @Captor.

(Spending hours of Googling and debugging.)

The root cause of NPE is that the test is using JUnit 5, but @RunWith is a JUnit 4 annotation.

@RunWith no longer exists; superseded by @ExtendWith.

Therefore, @RunWith(MockitoJUnitRunner.class) is totally ignored by JUnit 5, no setup happens for objects annotated by @Captor.

To support JUnit 5 @ExtendWith, Mockito provides a MockitoExtension class.

This extension is the JUnit Jupiter equivalent of our JUnit4 MockitoJUnitRunner.

Below code has no NPE, for test cases using JUnit 5 and Mockito annotations.

@ExtendWith(MockitoExtension.class)
class OrderTest {
    @Captor
    ArgumentCaptor<Offer> offerCaptor;

Some Thoughts

It would be better if JUnit 5 warns us if it finds the obsolete JUnit 4 @RunWith, instead of failing silently.

Another solution is that, if a project is going to use JUnit 5, just exclude the JUnit 4 from the dependencies of the project. So using @RunWith would be a compile error.