'Mockito's argThat returning null when in Kotlin

Given the following class (written in kotlin):

class Target {
     fun <R> target(filter: String, mapper: (String) -> R): R = mapper(filter)
}

I'm able to test in java, the test code:

@Test
public void testInJava() {
    Target mockTarget = Mockito.mock(Target.class);
    Mockito.when(mockTarget.target(
            argThat(it -> true),
            Mockito.argThat(it -> true)
    )).thenReturn(100);
    assert mockTarget.target("Hello World", it -> 1) == 100;
}

The java test pass as expected, but the same test is written in kotlin:

@Test
fun test() {
    val mockTarget = Mockito.mock(Target::class.java)
    Mockito.`when`(mockTarget.target(
            Mockito.argThat<String> { true },
            mapper = Mockito.argThat<Function1<String, Int>>({ true }))
    ).thenReturn(100)
    assert(mockTarget.target("Hello World") { 1 } == 100)
}

The kotlin version I receive the following exception:

java.lang.IllegalStateException: Mockito.argThat<String> { true } must not be null

Why is it happening and how can I test that using kotlin?



Solution 1:[1]

I also faced the same problem.

And finally, I found argThat() will return null, and normally the argument in the function in kotlin, does not accept null type.

The source code of argThat from ArgumentMatchers.java

public static <T> T argThat(ArgumentMatcher<T> matcher) {
    reportMatcher(matcher);
    return null;
}

You can see that it return null. So when we mock the function, it will throw IllegalStateException, because argThat returns null and argument can't be null.

It mean that if your function is:

fun doSomething(arg1: String): Int {
    // do something
}

When you mock it like that:

Mockito.`when`(
    doSomething(Mockito.argThat<String> { true })
).thenReturn(100)

It will throw IllegalStateException

So you should change your function like that:

fun doSomething(arg1: String?): Int {
    // do something
}

Change the "String" to "String?", make it accept null type.

My solution is to define the argument with class? so that it can accept null, but I don't know if it is a great solution

Solution 2:[2]

As of this writing, mockito-kotlin hasn't been updated for more than a year. As with all of these libraries, there's always a constant need for keeping them up-to-date, and I didn't want to get stuck with an unmaintained library.

So I came up with another way to solve the null issue with argThat without using any other libraries.

Say we've an interface UuidRepository as follows:

interface UuidRepository {
    suspend fun Entity save(entity: Entity): Entity
}

class Entity has two properties, userId: String and uuid: String.

The following code fails:

Mockito.verify(uuidRepository).save(argThat { it.userId == someValue && it.uuid == "test" })

with the error:

argThat { it.userId == someValue && it.uuid == "test" } must not be null

To solve this, we get all the invocation on the mock and then verify the ones we want:

val invocations = Mockito.mockingDetails(uuidRepository).invocations
    .filter { setOf("findById", "save").contains(it.method.name) }
    .map { it.method.name to it.arguments }
    .toMap()

assertThat(invocations).containsKey("save")
val first = invocations["save"]?.first()
assertThat(first).isNotNull
val entity = first as Entity
assertThat(entity.userId).isEqualTo(someValue)
assertThat(entity.uuid).isEqualTo("test")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Abhijit Sarkar