07-20-2023, 04:26 PM
Let's say there's an interface with a callback:
interface SomeInterface {
fun doSomething(arg: String, callback: (Exception?, Long) -> Unit)
}
which I extend into a suspend function like this:
suspend fun SomeInterface.doSomething(arg: String): Long = suspendCoroutine { cont ->
this.doSomething(arg) { err, result ->
if (err == null) {
cont.resume(result)
} else {
cont.resumeWithException(err)
}
}
}
I'd like to mock this in tests, but am failing.
Ideally I'd like to use something like this:
@Test
fun checkService() {
runBlocking {
val myService = mock<SomeInterface>()
whenever(myService.doSomething(anyString())).thenReturn(1234L)
val result = myService.doSomething("")
assertEquals(result, 1234L)
}
}
The above syntax fails with a mockito exception because it's expecting a matcher for the callback.
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
How can I mock a suspend function like that?
If a similar syntax is not possible how can I have the mock call back with the desired arguments such that the suspend variant that is used throughout my code returns the desired result during tests?
**Update**:
It seems it's not possible when it's an extension function. Based on [Marko Topolnik][1]'s comment, I gather it's because an extension is simply a static function which is out of mockito's capability.
When the suspend function is a member function, then it works as expected, with my original syntax.
Here is a gist with some demo code:
[1]:
interface SomeInterface {
fun doSomething(arg: String, callback: (Exception?, Long) -> Unit)
}
which I extend into a suspend function like this:
suspend fun SomeInterface.doSomething(arg: String): Long = suspendCoroutine { cont ->
this.doSomething(arg) { err, result ->
if (err == null) {
cont.resume(result)
} else {
cont.resumeWithException(err)
}
}
}
I'd like to mock this in tests, but am failing.
Ideally I'd like to use something like this:
@Test
fun checkService() {
runBlocking {
val myService = mock<SomeInterface>()
whenever(myService.doSomething(anyString())).thenReturn(1234L)
val result = myService.doSomething("")
assertEquals(result, 1234L)
}
}
The above syntax fails with a mockito exception because it's expecting a matcher for the callback.
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
How can I mock a suspend function like that?
If a similar syntax is not possible how can I have the mock call back with the desired arguments such that the suspend variant that is used throughout my code returns the desired result during tests?
**Update**:
It seems it's not possible when it's an extension function. Based on [Marko Topolnik][1]'s comment, I gather it's because an extension is simply a static function which is out of mockito's capability.
When the suspend function is a member function, then it works as expected, with my original syntax.
Here is a gist with some demo code:
[1]:
[To see links please register here]