Home > Mobile >  Implementing tests for a slowdown function with Kotlin Coroutines
Implementing tests for a slowdown function with Kotlin Coroutines

Time:11-30

I wrote a very simple slowdown function that can be used to slow down a response without blocking

    suspend inline fun <T> slowdown(duration: Duration, block: () -> T): T {
        var result: T
        val timeTaken = measureTime {
            result = block()
        }
        delay(duration - timeTaken)
        return result
    }

running it inside a main works correctly

fun main() = runBlocking {

    val timeTaken1 = measureTime {
        slowdown(2.seconds) {
            delay(1000)
        }
    }

    val timeTaken2 = measureTime {
        slowdown(2.seconds) {
            delay(4000)
        }
    }

    println(timeTaken1) // 2.008962891s
    println(timeTaken2) // 4.000145711s
    
}

Now I want to write a test case that will test the slowdown, but without actually taking 2 seconds or 4 seconds

    @Test
    @ExperimentalCoroutinesApi
    fun `ensure execution takes N seconds when operation takes N-t time`() = runTest {

        val timeTaken1 = measureTime {
            slowdown(2.seconds) {
                delay(1000)
            }
        }

        val timeTaken2 = measureTime {
            slowdown(2.seconds) {
                delay(4000)
            }
        }

        println("========")
        println(timeTaken1) // 9.472240ms
        println(timeTaken2) // 131.839us
        println("========")

    }

I'm assuming measureTime doesn't play nice with runTest, but works great with delay

If I replace delay with Thread.sleep in the test

        val timeTaken1 = measureTime {
            slowdown(2.seconds) {
                Thread.sleep(1000)
            }
        }

        val timeTaken2 = measureTime {
            slowdown(2.seconds) {
                Thread.sleep(4000)
            }
        }

then it prints out

========
1.008311216s
4.000221902s
========

delay(1000) happens instantly under runTest, but measureTime also measures it as instantly instead of being offset by 1 second.

I thought I could hack it using advanceTimeBy(), but it doesn't seem to affect measureTime.

Changing runTest to runBlocking works correctly, but it also slows down the tests to 2 and 4 seconds respectively.

Is there a measureTime that plays nicely with runTest and works under normal runBlocking ?

CodePudding user response:

suspend fun <T> slowdown(duration: Duration, block: suspend () -> T): T = coroutineScope {
    launch { delay(duration) }
    block()
}

@Test
@ExperimentalCoroutinesApi
fun `ensure execution takes N seconds when execution faster than N`() = runTest {
    val currentTime1 = currentTime
    slowdown(2.seconds) {
        delay(1.seconds)
    }
    val currentTime2 = currentTime
    assertEquals(2000, currentTime2 - currentTime1)
}

@Test
@ExperimentalCoroutinesApi
fun `ensure execution takes more than N seconds when execution slower than N`() = runTest {
    val currentTime1 = currentTime
    slowdown(1.seconds) {
        delay(2.seconds)
    }
    val currentTime2 = currentTime
    assertEquals(2000, currentTime2 - currentTime1)
}
  • Related