64

I am trying to test ViewModel to make sure livedata gets updated correctly. However when using ArgumentMatchers.any() it fails with IllegalStateException saying:

ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not be null

@Test
fun emitValueIfCountIs7() {
    doAnswer { invocation: InvocationOnMock ->
        val subscriber: mViewModel.CountSubscriber = invocation.getArgument(0)
        subscriber.onNext(7)
        null
    }.`when`(countUseCase).execute(
        ArgumentMatchers.any(mViewModel.CountSubscriber::class.java),
        ArgumentMatchers.any(Parameters::class.java)
    )
    
    // When
    mViewModel.getCount()
    
    // Verify
    assert(mViewModel.countResponse.value != null)
}

I am using Kotlin and have the following dependencies:

testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-inline:2.23.4"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"

Here are my imports:

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.doAnswer
import io.reactivex.Observable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.invocation.InvocationOnMock

Strange thing is that it used to work before, and I don't know what has happened that could affect this.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Ana Koridze
  • 1,532
  • 2
  • 18
  • 28

7 Answers7

85

Getting matchers to work with Kotlin can be a problem. If you have a method written in kotlin that does not take a nullable parameter, then we cannot match with it using Mockito.any(). This is because it can return void and this is not assignable to a non-nullable parameter.

If the method being matched is written in Java, then I think that it will work as all Java objects are implicitly nullable.

One possible solution would be to use a library like mockito-kotlin But you can solve this issue easily with a few lines of code yourself.

If you need typed any(type: Class)

private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)

OR

You can use this matcher instead of Matchers.any() :

object MockitoHelper {
fun <T> anyObject(): T {
    Mockito.any<T>()
    return uninitialized()
}
@Suppress("UNCHECKED_CAST")
   fun <T> uninitialized(): T = null as T
}

and use MockitoHelper.anyObject() instead of any() in your kotlin tests.

You can find more information in this post: Using Mockito with Kotlin

There is a discussion about possible solutions in this post : Is it possible to use Mockito in Kotlin?

Kristianmitk
  • 4,528
  • 5
  • 26
  • 46
Nabzi
  • 1,823
  • 1
  • 16
  • 26
  • 1
    Thank you very much for this explanation and these answers. The first typed class fn worked for me perfectly. – orpheus Oct 31 '21 at 19:12
  • 2
    This is the wrong answer. The correct answer is you just need to use `import kotlin.any` instead of `import mockito.any` and it works. – Alex Arvanitidis Aug 31 '22 at 08:04
  • This does not work (any more?) - it seems the kotlin compiler is now smart enough to not be confused by the unchecked cast of null to T – Bastian Voigt Sep 07 '22 at 08:47
  • If your class have its own type then you are fucked. The only way would be using `mockito-kotlin` – Farid Sep 23 '22 at 06:58
62

The correct solution is mentioned in the comment section of the question by @Ana Koridze. Yes, if you are using Koltin + mockitoKotlin. Make sure you are using the following import:

1 - Using the Mockito-kotlin:

import org.mockito.kotlin.any from Mockito-kotlin instead of import org.mockito.Mockito.any

testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"

2 - Or if you are using older mockito kotlin version original created by nhaarman before the intergation

import com.nhaarman.mockitokotlin2.any from nhaaram's Mockito-kotlin instead of import org.mockito.Mockito.any

testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin2_version"

BTW, if you are using Android Studio or IntelliJ IDEA. the any() from mockitokotlin library should be italic by default style/color scheme.


Notice the any() at the end of line. This is from mockitokotlin

enter image description here

And here is the any() from mockito

enter image description here

Thanks @Sattar for the recommended edit.

Haomin
  • 1,265
  • 13
  • 12
12

mockito-kotlin has added support for nullable args with anyOrNull()

        `when`(myMock.doesThis(any(), anyOrNull())).thenReturn(someObject)
jony.buzz
  • 143
  • 2
  • 7
  • 1
    note that you can use `whenever` which is an elegant alias to `when` (reserved keyword in Kotlin but not in Java), so you can avoid the slightly harder to read escaping. – Gregor Dec 19 '22 at 15:19
6

use Mockito-kotlin

testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"

this will use any that works for kotlin as this is a wrapper lib for Mockito but for kotlin

Sattar
  • 2,453
  • 2
  • 33
  • 47
2

This is what worked for me,

  1. either replaced all generic any()s , with a specific anyTYPE(), i.e anyInt(), anyList() etc from core lib org.mockito:mockito-core and it fixes the (nullability)issue, it seems the specific definitions with type can handle null. this option does not require you to import any extra lib

or

  1. if you really need to use the generic type any() , add this official Mckito extension lib https://github.com/mockito/mockito-kotlin and make sure you use the any() from this lib (by making sure your imports has this in it import org.mockito.kotlin.any)

I suggest to start using MockK lib https://github.com/mockk/mockk instead of Mockito as it is a Mock library for Kotlin = MockK)

however, if you feel lazy to switch right now or maybe dealing with legacy tests (as in my case :), this should fix your issue too.

bastami82
  • 5,955
  • 7
  • 33
  • 44
2

For me all solutions above were not enough - in addition to that I had to mark the called method as an 'open' method. According to this: https://github.com/mockito/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null The method is final and Mockito couldn't mock it so I had to add 'open'.

Gad Wissberg
  • 455
  • 1
  • 6
  • 19
2

I wrote a simple wrapper function around Mockito's any() and got rid of the warning.

private fun <T> any() : T {
    return org.mockito.ArgumentMatchers.any()
}

I am still pretty green at Kotlin though, so I am not sure whether there may be some unwanted side effects. I put this answer out there in case it helps anyone or someone gives me feedback.