Befriending Kotlin and Mockito

Kotlin and Java are very close friend. A brotherly relationship (although we know Kotlin had the intention are replace Java in Android someday :P). Mockito is a close friend of Java, so naturally it should work well with Kotlin.

But in my experience, I faced several hiccups in their relationship. So we need some work to help mend the relationship :)

Below are some codes to illustration the tensed relationship (you’ll need to run it to see the errors). Firstly the source code of class to be unit tested i.e. SimpleClass and it’s other related classes.

// CLASS FUNCTION TO BE TESTED
class SimpleClass(val injectedClass: InjectedClass) {
fun usingFunction() {
injectedClass.usingDependentObject()
}
    fun settingFunction() {
injectedClass.settingDependentObject(DependentClass())
}
}
// CLASS TO BE MOCKED
class InjectedClass() {
lateinit var dependentObject: DependentClass
    fun settingDependentObject(dependentObject: DependentClass) {
this.dependentObject = dependentObject
}
    fun usingDependentObject() {
this.dependentObject.testFunction()
}
}
// CLASS AS PARAMETER OBJECT
class DependentClass() {
fun testFunction() {
    }
}

And then the Test Class

class SimpleClassTest {
    lateinit var simpleObject: SimpleClass
@Mock lateinit var injectedObject: InjectedClass

    @Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
    @Test
fun testUsingFunction() {
simpleObject = SimpleClass(injectedObject)
simpleObject.usingFunction()
        verify(injectedObject).usingDependentObject()
}
    @Test
fun testSettingFunction() {
simpleObject = SimpleClass(injectedObject)
simpleObject.settingFunction()
        verify(injectedObject).settingDependentObject(any())
}
}

When triggered the test run, various issue will surface… so let’s fix them one by one.

To be friendly, need to be Open

The first error seen as below

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.packagename.InjectedClass
Mockito cannot mock/spy because :
— final or anonymous class

Kotlin need to Open it’s Class

Unlike Java, by default, Kotlin’s class is final. A final class can’t be mocked. To overcome this, one could either consider using Interface to be implemented on the target mock, or just simply Open the class. For me, I just Open the InjectClass, since that’s the default way of doing in the past in Java. Anyway, to make friend, one need to be more open.

Kotlin need to Open it’s Function

After the above fix, the first error goes away. Then the second issue as below surface…

kotlin.UninitializedPropertyAccessException: lateinit property dependentObject has not been initialized
at com.packagename.InjectedClass.usingDependentObject(SimpleClass.kt:23)
at com.packagename.SimpleClass.usingFunction(SimpleClass.kt:6)
at SimpleClassTest.testUsingFunction(SimpleClassTest.kt:30)

This error is more cryptic than the previous one. It is complaining the dependentObject in usingDependentObject function is not initialized yet.

But wait, we have mocked InjectedClass, so it shouldn’t even trigger any of the content of the usingDependentObject function. Hence the issue is the actual function is now being used instead of the mocked version.

So to solve the problem, we’ll need to Open those functions of the mocked object that need to be verified. This will then have the function mocked call instead of the actual content called. So settingDependentObject and usingDependentObject need to be open.

Lesson 2 learned, making friend, not only need to open outwardly, also inwardly, but limited to those you want to be verified.

To be friendly, both sides need to give in.

Now, when the test is run, the first test i.e. testUsingFunction passed. Hurray! … But we still fail on testSettingFunction as below :(

java.lang.IllegalStateException: anyObject() must not be null
at SimpleClassTest.testSettingFunction(SimpleClassTest.kt:40)

Null Safety of Kotlin Issue

Checking it, this is Kotlin specific feature issue. The settingDependentObject function is only taking a Non-Null parameter! However, in Mockito, both anyObject() and any() will return Null in it’s verification function when being used.

We now might think, maybe we could change the function to be Nullable. But Kotlin side has done enough. We have force it to be so Open, and it no longer want to tolerate more changes. It now insist wanting to keep it’s Null-Safe feature… No more change on the source code.

Given that, it’s the test side that need to handle it now.

There are 3 approaches attempted as below.

Using Mockito-Kotlin

There’s a library https://github.com/nhaarman/mockito-kotlin which seems to address the Non-null issue. Great!! … But… that only works for parameter that have default constructor.

When verify a function that takes an object that doesn’t have default constructor, it will crash as below.

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at kotlin.reflect.jvm.internal.FunctionCaller$Constructor.call(FunctionCaller.kt:63)
at kotlin.reflect.jvm.internal.KCallableImpl$DefaultImpls.call(KCallableImpl.kt:67)

Construct Actual Object

Despite not proceeding with the library, it is handy as it provides some clue of how we could tackle this. When inspect inside nhaarman’s library, It uses Mockito.any function to determine if it is null, to create an instance of the object through it’s default constructor.

inline fun <reified T : Any> any() = Mockito.any(T::class.java) ?: createInstance<T>()

Using this approach, we could then specifically apply to a real object that we need in replacement of the default any() i.e.

inline fun <reified T : Any> any() = Mockito.any(DependentClass::class.java) ?: DependentClass()

This solve our problem, and it also resolve the nhaarman’s library issue we faced above.

We could move on…. But this is not as nice though, as it is so specific to the class object. So if we have multiple different object, we’ll need to tackle them individually or we need some generic hashmap that store this object that we need to explicitly create.

It does the job but not nice… So the mission is still on.

Casting Null to Object Type

Finally after explore further, found a much cleaner and better way of handling the issue. This is basically casing the Null Object into the specific class object using Generic. The code as below, where we override any() to handle the issue accordingly.

private fun <T> any(): T {
Mockito.any<T>()
return uninitialized()
}
private fun <T> uninitialized(): T = null as T

With this in place, the non null issue tackled, it’s generic on any object, and also won’t have problem even if the object doesn’t have default constructor.

Hence the Mockito side now can take the extra mile for establish the friendship with Kotlin, after several attempts above.

The “Friendly” Code

After all the effort, the source code now is as below (bold on the change)

// CLASS FUNCTION TO BE TESTED
class SimpleClass(val injectedClass: InjectedClass) {
fun usingFunction() {
injectedClass.usingDependentObject()
}
    fun settingFunction() {
injectedClass.settingDependentObject(DependentClass())
}
}
// CLASS TO BE MOCKED
open class InjectedClass() {
lateinit var dependentObject: DependentClass
    open fun settingDependentObject(dependentObject: DependentClass) {
this.dependentObject = dependentObject
}
    open fun usingDependentObject() {
this.dependentObject.testFunction()
}
}
// CLASS AS PARAMETER OBJECT
class DependentClass() {
fun testFunction() {
    }
}

and the test code now is as below (bold on the change)

class SimpleClassTest {
    private fun <T> any(): T {
Mockito.any<T>()
return uninitialized()
}
    private fun <T> uninitialized(): T = null as T
    lateinit var simpleObject: SimpleClass
@Mock lateinit var injectedObject: InjectedClass

    @Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
    @Test
fun testUsingFunction() {
simpleObject = SimpleClass(injectedObject)
simpleObject.usingFunction()
        verify(injectedObject).usingDependentObject()
}
    @Test
fun testSettingFunction() {
simpleObject = SimpleClass(injectedObject)
simpleObject.settingFunction()
        verify(injectedObject).settingDependentObject(any())
}
}

Both tests all passed now :)


Note that this between Kotlin and Mockito with test also written in Kotlin. If we embedded Java into it, this might trigger a triangular relationship… which might have other issues… But let’s not worry about it now, just convert any Java code to Kotlin will do (as mention earlier, one of Kotlin’s mission is to replace Java :P)