Migration to Mockito 2.1

Recently we got a great news.

They released Mockito 2.1.0 - after so many years of hopeless waiting!

There is a lot of cool updates in Mockito 2, including:

Yes! I must use it! - I decided.

What a huge disappointment it was for me…

Epic fail

I tried to upgrade my working project to Mockito 2.1.0, and … ups.

I got over 100 red tests (from ~6000).

My first emotion was - what the hell! Mockito 2 sucks!

But the further investigation showed that Mockito 2 is cool, but some of my tests are bad. The new Mockito found a whole bunch of problems in our tests.

Let’s discover them together.

What you need to do

Below you can find my tutorial for migration to Mockito 2.

Let’s start!

First, you will need to replace import

A lot of imports. In many files. Replace these lines:

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyVararg; // not needed - replaced by any()

by these lines:

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;

Second, you will update API of doAnswer

Was:

when(userDeviceService.save(any(UserDevice.class)))
    .then(invocation -> invocation.getArgumentAt(0, UserDevice.class));

Now:

when(userDeviceService.save(any()))
    .then(invocationOnMock -> invocationOnMock.getArgument(0));

(getArgumentAt -> getArgument, remove the class)

Third, you update API of isMock

Was:

import org.mockito.internal.util.MockUtil;

assertFalse(new MockUtil().isMock(expected));

Now:

import static org.mockito.internal.util.MockUtil.isMock;

assertFalse(isMock(expected));

The last version is really better, don’t you think? No need to create an unused object.

What problems you will experience

Some of your tests will break when you upgrade to Mockito 2.1. Below you will find some of the reasons.

1) Matcher any() does not match null anymore

This code worked before:

doNothing().when(service).calculateAmounts(any(BigDecimal.class), anyString());

But doesn’t work anymore, if some of parameters are null. Yes, my friend! You will need to rework all your tests that pass null instead of realistic value.

And it’s great, because the new code is really better:

doNothing().when(service).calculateAmounts(order.amount, order.currency);

Or this way:

doNothing().when(service).calculateAmounts(new BigDecimal("100.00"), "EEK");

If you really need to pass null parameter, you can still do it. Just pass null explicitly using isNull matcher:

doNothing().when(service).calculateAmounts(any(BigDecimal.class), isNull());

2) Matcher anyInt() does not match long anymore

This code worked with Mockito 1.x, but fails with Mockito 2.x:

when(mqService.send(anyString(), anyInt())).thenReturn("transfer-ref");

You need to replace anyInt() by anyLong() for Mockito 2.1:

 when(mqService.send(anyString(), anyLong())).thenReturn("transfer-ref");

Yes, my friend. You will need to rework all your tests that pass int instead of long etc. But it’s great, don’t you think? Because these tests were inaccurate.

3) You will discover bad tests

Yes, my friend. You will discover lot of bad lines among your tests. Just bad. Like this one:

@Test
public void checkMoratoriumRunsSilentlyWhenNoMoratorium() {
  doReturn("false").when(service).parseMoratoriumMessage(any(Mandate.class), any(LoanApplication.class));
  ...
  service.checkForMoratorium(any(Mandate.class), any(LoanApplication.class)); // WHAAAAAAAT?
  ...
}

This code worked with Mockito 1.x. Suddenly. But it fails with Mockito 2.1 - and it’s great, don’t you think?

Obviously, we should use mock instead of any in the second line:

service.checkForMoratorium(mock(Mandate.class), mock(LoanApplication.class));

By the way, even better solution is to avoid both mock and any and just create plain objects:

service.checkForMoratorium(new Mandate(), new LoanApplication());

4) You will discover a lot of sloppy tests

… that check only some parameters and don’t discover that others are null.

For example:

doReturn(user).when(loginService).tokenLogin(eq("bob"), eq("login-key"), anyString());
    
security.login("bob", "login-key", null);

As you see, tests passes null parameter in the second line. And only null! I discovered that there was no test that would pass something different from null there.

Mockito 2.1 will fail with such a sloppy test, because anyString() matcher doesn’t allow null anymore.

The newer test that works with Mockito 2.1 is actually more precise:

    request.remoteAddress = "127.0.0.2";
    doReturn(user).when(loginService).tokenLogin("bob", "login-key", "127.0.0.2");
    ...

You see it? You don’t need all these obscure eq and anyString. Much better!

5) You will discover mystical red tests

You will find some red tests for which it’s really hard to understand why they fail.

For example:

@Test
public void requestByReferenceNumberNeverCreatesSubscription() {
  RequestByReferenceNumber requestByReferenceNumber = new RequestByReferenceNumber(user, "12345678901234567890");
  when(gisgmpService.request(any(RequestByDrivingLicense.class))).thenReturn(requestByReferenceNumber);

  GISGMP.requestCharges("12345678901234567890");
  ...

It took quite a long time to solve this issue. I could not find out why this test started to fail with Mockito 2.

Please note the second line. Obviously authors wanted to write any(RequestByReferenceNumber.class) instead of any(RequestByDrivingLicense.class) (they both inherit the same superclass).

It seems to be a bug of Mockito 1: it allowed using any(AWrongClass.class), and this incorrect test have been green for several years. :(

6) You will find out that anyList() and anyCollection() are now different things

For example, this code worked with Mockito 1:

  @Test
  public void domesticPaymentInForeignCurrencyCanBeEnabled() {
    doCallRealMethod().when(Payments.accountService).forOperation(anyList(), eq(DOMESTIC));

    Collection<Account> accounts = ...
    
    return accountService.forOperation(accounts, DOMESTIC);

Note that mock in the first line uses anyList(), but the code actually passes variable accounts of type Collection (thought it’s actually List).

Mockito 2 doesn’t allow it anymore. You need to mock more precisely using anyCollection().

And you will be happy

To summarize: you will see a lot of pain, but you will become happy. The tests got better, the world got lighter.

Peace 2.1.0!

Andrei Solntsev

asolntsev.github.io