Description
Hi,
I have two functions in kotlin
b.first()
b.second()
I want to execute some code after first runs and second goes.
For that I use mutexes. I do something like:
doAnswer {
lock1.unlock()
runBlocking {
lock2.lock()
}
it.callRealMethod()
}.whenever(b).second()
And then I can do:
// first executed here
lock1.lock()
// whatever code I want
lock2.unlock()
// second will be executed here
And it works.
Now, in my case, to make my code more stable, I want to modify only first.
And I do something like
doAnswer {
it.callRealMethod().also {
runBlocking {
lock1.unlock()
lock.lock()
}
}
}.whenever(b).first()
This doesn't work. second will be executed before the code between the locks. My guess is that this is because there is some kotlin coroutine change and I'm blocking the wrong coroutine.
Reproducible example:
class B {
suspend fun first() {
withContext(currentCoroutineContext()) {
async {
println("first")
}
}
}
suspend fun second() {
println("second")
}
}
class A (val b: B) {
suspend fun both() {
b.first()
b.second()
}
}
@Test
fun `Coordination test`() = runBlocking<Unit> {
val lock1 = Mutex(true)
val lock = Mutex(true)
val b = spy(B())
val a = A(b)
// NOT working
doAnswer {
it.callRealMethod().also {
runBlocking {
lock1.unlock()
lock.lock()
}
}
}.whenever(b).first()
/*
WORKING
doAnswer {
lock1.unlock()
runBlocking {
lock2.lock()
}
it.callRealMethod()
}.whenever(b).second()
*/
async {a.both()}
delay(1000)
lock1.lock()
println("in between")
lock.unlock()
}
This example prints
first
second
in between
If I spy on second it works as expected
first
in between
second
And if I remove the async in first method, it also works as expected. Which for me is a hint that we are blocking the wrong coroutine. Probably meaning that mockito is doing the continuation on it.callRealMethod() instead on doAnswer{}. Is there any workaround?