https://kotlinlang.org logo
#coroutines
Title
# coroutines
m

myanmarking

09/13/2019, 12:45 PM
Copy code
class TestExample {

    class Example {
        var counter: Int = 0

        fun startCounter() {
            val coroutineScope = CoroutineScope(Dispatchers.Unconfined)

            coroutineScope.launch {
                while (counter <= 100) {
                    counter += 1
                    delay(100)
                }
            }
        }
    }

    @Test
    fun `test 1`() = runBlockingTest {
        val example = Example()
        example.startCounter()
        Assert.assertEquals(1, example.counter)
        advanceTimeBy(150)    // fails
        // Thread.sleep(150) // works
        Assert.assertEquals(2, example.counter)
    }
}
p

Paulius Ruminas

09/13/2019, 12:59 PM
You override the coroutine test scope
val coroutineScope = CoroutineScope(Dispatchers.Unconfined)
so you must wait for 100ms.
m

myanmarking

09/13/2019, 12:59 PM
what do you mean ?
p

Paulius Ruminas

09/13/2019, 1:02 PM
Copy code
class Example {
        var counter: Int = 0

        suspend fun startCounter(scope: CoroutineScope) {
            scope.launch {
                while (counter <= 100) {
                    counter++
                    delay(100)
                }
            }
        }
    }

    @Test
    fun `test 1`() = runBlockingTest {
        val example = Example()
        example.startCounter(this@runBlockingTest)
        Assert.assertEquals(1, example.counter)
        advanceTimeBy(150)
        Assert.assertEquals(2, example.counter)
    }
runBlockingTest
creates a coroutine scope for running tests
val scope = TestCoroutineScope(safeContext)
. If you do not reuse this scope then you don't have a virtual clock anymore and you actually have to wait for the specified delay.
m

myanmarking

09/13/2019, 1:10 PM
i'll try the suggestion. thanks!
it works. thanks! is there any way to make it work with the original code ?
g

gergo

09/13/2019, 1:38 PM
not really, only if you inject the Dispatcher or the CoroutineScope or the delay value so you can switch it out in the test
m

myanmarking

09/13/2019, 1:39 PM
hm, in case i inject the CoroutineScope, won't multiple function calls use the same scope instance ? i'm not sure about this semantics
g

gergo

09/13/2019, 1:41 PM
yes they would, but i don't see when this would be an issue for you
If you really want new scope every time and inject the scope, then you could inject a factory like
class Example(private val coroutineScopeFactory : () -> CoroutineScope = { CoroutineScope(Dispatchers.Unconfined) })
... then in test you could swich it out like
Example{ this@runBlockingTest }
in your method would look like
val coroutineScope = coroutineScopeFactory()
m

myanmarking

09/13/2019, 1:45 PM
ya, thanks
3 Views