Charan
Charan

Reputation: 7

how to unit test coroutine with multiple delay functions

I followed https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/ , looks like runBlockingTest is skipping the delay coroutine and I’m not able to reach the code after delay from main function. Is there a good way unit test/advance the timer and assert flags accordingly?

can you please help me to unit test my main function

// my main function is having multiple delay coroutines to set a flag true/false
fun delayFunction() {
    launch { // coroutine scope
        isFlag = false
        delay(1000)
        isFlag = true
        delay(1000)
        isFlag = false
    }
}

// I tried unit testing something like this 
@Test
fun `test delayFunction`() {
    runBlockingTest {
        delayFunction()
        advanceTimeBy(1001)
        assertThat(isFlag).isTrue()
        advanceTimeBy(1001)
        assertThat(isFlag).isFalse()
    }
}

Upvotes: -1

Views: 1868

Answers (1)

Armend Ukehaxhaj
Armend Ukehaxhaj

Reputation: 657

For anyone looking into this problem, you can check out: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/, specifically Controlling the virtual time where you can check in-between delays:

@Test
fun testFoo() = runTest {
    launch {
        val workDuration = testScheduler.timeSource.measureTime {
            println(1)   // executes during runCurrent()
            delay(1_000) // suspends until time is advanced by at least 1_000
            println(2)   // executes during advanceTimeBy(2_000)
            delay(500)   // suspends until the time is advanced by another 500 ms
            println(3)   // also executes during advanceTimeBy(2_000)
            delay(5_000) // will suspend by another 4_500 ms
            println(4)   // executes during advanceUntilIdle()
        }
        assertEquals(6500.milliseconds, workDuration) // the work took 6_500 ms of virtual time
    }
    // the child coroutine has not run yet
    testScheduler.runCurrent()
    // the child coroutine has called println(1), and is suspended on delay(1_000)
    testScheduler.advanceTimeBy(2.seconds) // progress time, this will cause two calls to `delay` to resume
    // the child coroutine has called println(2) and println(3) and suspends for another 4_500 virtual milliseconds
    testScheduler.advanceUntilIdle() // will run the child coroutine to completion
    assertEquals(6500, currentTime) // the child coroutine finished at virtual time of 6_500 milliseconds
}

For my case, I had to something similar:

@Test
fun testFoo() = runTest {
    launch {
        myMethodWithDelaysInside()
    }

    testScheduler.runCurrent()

    // Assert my flag here
    assertTrue(myFlag)

    testScheduler.advanceTimeBy(2.seconds) // progress time, this will cause two calls to `delay` to resume
    
    // Assert that my flag has changed
    assertFalse(myFlag)
}

Upvotes: 1

Related Questions