Search code examples
kotlinmockitopowermockrobolectricmockito-kotlin

How to mock Kotlin's List.forEach?


For the given code below, in Kotlin, it's not possible to execute the function forEach (or anything similar, map, filter, so on...) because BarList is mocked. So the native implementation of forEach and relatives are not being called. After a bit of search on the internet I came with this:

public class BarList<E> extends AbstractList<E> implements OrderedBarCollection<E> { 
    //...
}
//...

val mockedList: BarList<Foo> = mockBarList()
val fooList = listOf(Foo())

`when`(mockedList.iterator()).thenReturn(fooList.iterator())
`when`(mockedList.size).thenReturn(fooList.size)

com.nhaarman.mockito_kotlin
    .doCallRealMethod()
    .`when`(mockedList)
    .forEach(Mockito.any<Consumer<Foo>>()) //Attempt to call the Consumer.

mockedList.forEach { foo ->
    //Not executing
}

I tried this above based on the answer of this question: https://stackoverflow.com/a/49407513/2430555

I also tried:

com.nhaarman.mockito_kotlin.doCallRealMethod().`when`(mockedList).forEach(any())

But it's causing:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
//...

The definition of forEach in the Kotlin source code is:

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit

I think I should replace any() with something that matches action: (T) -> Unit, but I am not sure how to do this. I don't mind to use interactor if needed, but I need to at least make forEach run as expected. Could you guys please help me?

Regards,

Pedro


Solution

  • The solution is to use thenAnswer instead of thenReturn for the mocked list.

    `when`(mockedList.iterator()).thenAnswer { fooList.iterator() }
    

    Reason:

    Author: Tobias Berger

    an iterator is only good for going through the list once. Therefore you usually get a new one every time you call the iterator() function. If you mock that with thenReturn(messageList.iterator()) , it will just call that once on messageList and reuse that iterator instance every time you try to get one for your mock. Once your first loop over this iterator is finished, it will always say it has no more items. With thenAnswer you define a supplier that is called each time your mocked function is used, providing a new iterator for each call (as would be expected)