21

My code as below, refering to the solution in https://stackoverflow.com/a/30308199/3286489

import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.*

class SimpleClassTest {

    private fun <T> anyObject(): T {
        Mockito.anyObject<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 testSimpleFunction() {
        simpleObject = SimpleClass(injectedObject)

        verify(injectedObject).settingDependentObject(anyObject())

    }
}

I still have the below error

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method my.package.InjectedClass.settingDependentObject, parameter dependentObject

Did I miss anything?

UPDATED Below is the code tested (simplest form and working)

class SimpleClass(val injectedClass: InjectedClass) {

    fun simpleFunction() {
        injectedClass.settingDependentObject(DependentClass(Response.Builder().build()))
    }
}

open class DependentClass(response: Response) {

}

open class InjectedClass() {
    lateinit var dependentObject: DependentClass

    fun settingDependentObject(dependentObject: DependentClass) {
        this.dependentObject = dependentObject
    }
}
Community
  • 1
  • 1
Elye
  • 53,639
  • 54
  • 212
  • 474

4 Answers4

16

By default Kotlin classes and members are final. Mockito cannot mock final classes or methods. Thus when you write:

verify(injectedObject).settingDependentObject(anyObject())

the real implementation is called which requires non null argument.

To fix that either open your class and method or, even better, change SimpleClass to accept an interface as its constructor argument and mock the interface instead.

miensol
  • 39,733
  • 7
  • 116
  • 112
  • 1
    My injectObject class is already an open class. The settingDependentObject require a non-null object, but anyObject() returns null. That's why I thought the solution in http://stackoverflow.com/a/30308199/3286489 would help me overcome the null required issue. But it's still not working. – Elye May 21 '16 at 08:20
  • @Elye please add the `InjectedClass` source to the question – miensol May 21 '16 at 08:48
  • Added the class to be tested to the Update above. Thanks! – Elye May 21 '16 at 09:33
  • 4
    @Elye you need to open both class and method for Mockito to work – miensol May 21 '16 at 10:20
  • Ya, open settingDependentObject, makes the things work great. Thanks! – Elye May 21 '16 at 10:50
  • @Elye Opening your classes/methods just for testing makes no sense – Farid Sep 23 '22 at 07:05
1

There is a project specifically to help deal with Kotlin "closed by default" in unit testing with Mockito. For JUNIT, you can use the kotlin-testrunner which is an easy way to make any Kotlin test automatically open up classes for testing as they are loaded by the classloader. Usage is simple, just add one annotation of @RunWith(KotlinTestRunner::class), for example:

@RunWith(KotlinTestRunner::class)
class MyKotlinTestclass {
   @Test 
   fun test() {
   ...
   }
}

This is thoroughly covered in the article Never say final: mocking Kotlin classes in unit tests

This covers your use case in an automatic way by allowing all classes to be mocked that otherwise would not be allowed.

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
0

I ran into the same issue with Mockito when using RETURNS_DEEP_STUBS. It seems like nulls are still returned for nested objects, even when using the kotlin-allopen plugin.

Please check out and comment on this issue on Mockito if you're having the same problem.

fast3r
  • 1,298
  • 13
  • 15
-1

You can use this function instead

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