Skip to content

How to control continuation after it.callRealMethod() #537

Open
@furstenheim-goodnotes

Description

@furstenheim-goodnotes

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions