Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Migration to Mockito 2.1

DZone's Guide to

Migration to Mockito 2.1

With the long-awaited release of Mockito 2.1, it's clear the team put a lot more thought into their work. See what you can't get away with anymore (and be ready to see a lot).

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

Recently we got a great news. Mockito 2.1.0 has been released — after so many years of hopeless waiting!

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

  • Support for Java 8.

  • Migration from CGLIB to ByteBuddy.

  • Mocking final classes and methods.

"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 … oops. I got over 100 red tests (from ~6,000). My first thought 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 the Imports

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()

With these lines:

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


Second, You Will Update the API of doAnswer

It was:

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

Now, it's:

(getArgumentAt -> getArgument, remove the class)

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


Third, Update the API of isMock

It was:

import org.mockito.internal.util.MockUtil;

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

Now, it's:

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.

The 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() Doesn't Match Null Anymore

This code worked before:

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


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

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

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


Or would this be even better?

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


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

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


2) Matcher anyInt() Doesn't 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() with 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 a 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. 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, an 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, as you can see, this passes the null parameter in the second line. And only null! I discovered that there was no test that would pass something different from null there.

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

security.login("bob", "login-key", null);


Mockito 2.1 will fail with such a sloppy test because the 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, the 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 that's incorrect. Unfortunately, that test has 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 (though 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 be happier for it.

The tests got better, the world got lighter.

Peace 2.1.0!

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
unit tests ,mockito ,java ,code quality

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}