Fixing Upgrade Mockito 1.x to 2.x issues

Written by elye.project | Published 2017/09/07
Tech Story Tags: android-app-development | androiddev | android | mobile-app-development

TLDRvia the TL;DR App

With Kotlin in place, in order to support Mocking Kotlin class without opening it, we would require Mockito 2.0. Check out my blog on the detail. In migrating from Mockito 1 to Mockito 2, faced two issues that need some exploration before one could solve it. Hence shared it here.

Note: In our scenario here, the below is used

testCompile "org.mockito:mockito-inline:2.8.47"

1. Distinguish of Null and Non-Null item

In Mockito 1.x, when we are verify of define when scenario of any argument, we would just typed it accordingly such as

when(myMock.myBoolean(Mockito.any(MyClass.class)).thenReturn(true);
verify(myMock).myFunction(Mockito.any(String.class));
//or in some case
verify(myMock).myFunction(Mockito.anyString());

When you migrate over to Mockito 2.x, this still works without compile warning. However when you run, some test will start failing.

This is because, now for any(Class<T> Type) is defined to be explicitly needing T object that is Non-Null. If a null is passed in instead, it will fail the matching.

So to overcome this issue, as per stated by Mockito Issue, use this below

when(myMock.myBoolean(Mockito.<MyClass>any()).thenReturn(true);
verify(myMock).myFunction(Mockito.<String>any());

This will allow both the Null and NonNull matching. Well, you might only want to do this if this is really a nullable argument.

2. Inability to Mock

Some class no longer mockable. One most distinct example is FirebaseRemoteConfig. The error as below.

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.google.firebase.remoteconfig.FirebaseRemoteConfig.

If you’re not sure why you’re getting this error, please report to the mailing list.

Java : 1.8
JVM vendor name : JetBrains s.r.o
JVM vendor version : 25.152-b01
JVM name : OpenJDK 64-Bit Server VM
JVM version : 1.8.0_152-release-915-b01
JVM info : mixed mode
OS name : Mac OS X
OS version : 10.12.6

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

It is reported in Mockito Issue, and is still open.

The way to workaround this, is to wrap the class with an Interface Helper class.

Hence if you are originally mocking

@Mock
FirebaseRemoteConfig firebaseRemoteConfig;

Now you could

@Mock
FirebaseRemoteConfigHelperInterface firebaseRemoteConfig;

The implementation interface could just wrap around the actual FirebaseRemoteConfig object, and implement only those function that it uses. e.g.

public class FirebaseRemoteConfigHelperImpl implements 
FirebaseRemoteConfigHelperInterface {
    private FirebaseRemoteConfig firebaseRemoteConfig

    public FirebaseRemoteConfigHelperImpl(
         FirebaseRemoteConfig firebaseRemoteConfig) {
         this.firebaseRemoteConfig = firebaseRemoteConfig
    }

    // Follow by all the implementation function of 
    // firebaseRemoteconfig used
}

With that, this is now mockable.

It is not an ideal solution, but there shouldn’t be too many of such classes is un-mockable. So we’ll live with that, but I’m open for better solution idea to be shared.

If you like my post and want to get future update, follow me at medium~Twitter:elye; Facebook:elye


Published by HackerNoon on 2017/09/07