DEV Community

Cover image for Matt's Tidbits #82 - An unexpected Mockito limitation
Matthew Groves
Matthew Groves

Posted on • Originally published at Medium

1

Matt's Tidbits #82 - An unexpected Mockito limitation

Last week I wrote about the importance of taking vacation. This time, I have a story to share about an unexpected Mockito limitation.

Consider this example code:

interface ListProvider {
val listObservable: Observable<List<String>>
val currentList: List<String>
...
}
class ListProviderImpl: ListProvider {
private var listBehaviorSubject: BehaviorSubject<List<String>> = BehaviorSubject.create()
override val listObservable: Observable<List<String>>
get() = listBehaviorSubject
override val currentList: List<String>
get() =
// This is set up this way so it's always a non-blocking synchronous call
if (listBehaviorSubject.hasValue())
listBehaviorSubject.value
else
emptyList()
...
}

If you want to mock out the ListProvider in your unit tests, it's pretty simple to do the following:

val mockListProvider: ListProvider = Mockito.mock(ListProvider::class.java)
val listSubject: BehaviorSubject<List<String>> = BehaviorSubject.create()
whenever(mockListProvider.listObservable).thenReturn(listSubject)

Where this gets tricky is when you think about providing a mock implementation for ListProvider.currentList. At first, I thought about just doing this:

whenever(mockListProvider.currentList).thenReturn(listSubject.value)
Enter fullscreen mode Exit fullscreen mode

The problem with this is that if you post a new value to the listSubject, it will not be reflected in future calls to ListProvider.currentList because the mock (and by extension, the underlying subject) is evaluated at the time it is defined.

One workaround I came up with was to define a method like this for tests:

private fun updateList(list: List<String>) {
listSubject.onNext(list)
whenever(mockListProvider.currentList).thenReturn(list)
}

Using this instead of the listSubject directly allows you to update the mock value for currentList simultaneously.

However, this has some limitations - namely, as a coworker pointed out, it's a little bit messy to re-initialize the mock each time.

Thankfully, Mockito provides another option that's a little bit better suited to this - enter the thenAnswer() method:

whenever(mockListProvider.currentList).thenAnswer { listSubject.value }
Enter fullscreen mode Exit fullscreen mode

Something to be careful of with this method is that it's not type safe - it would be perfectly valid to write .thenAnswer { 42 }, and you would only find out when running your tests that you had done something wrong.

One more thing to consider - Mockito isn't the most Kotlin-friendly framework out there, so if the majority of your code is in Kotlin, I would highly encourage you to consider MockK, which lets you write this same code like this:

val mockListProvider = mockk<ListProvider> {
val listSubject: BehaviorSubject<List<String>> = BehaviorSubject.create();
every { listObservable } returns listSubject
every { currentList } answers { listSubject.value }
}

Do you have any ideas/suggestions for how to further improve on this? Let me know in the comments below! And, please follow me on Medium if you're interested in being notified of future tidbits.

This tidbit was discovered on June 25, 2020.

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay