bezrukov
01/14/2022, 2:22 PMbezrukov
01/14/2022, 2:26 PMfun interface SomeDependency {
fun call(x: Int)
}
class TestLoop(
scope: CoroutineScope,
private val dependency: SomeDependency,
) {
private val actor = scope.actor<Int> {
for (x in channel) {
dependency.call(x)
}
}
fun update(x: Int) {
actor.trySend(x)
}
}
This test will hang forever (all assertion will be checked successfully) but runTest
will wait for an actor completion because it is launched in that scope
@Test
fun test() = runTest(UnconfinedTestDispatcher()) {
var dependencyValue: Int = 0
val dependency = SomeDependency {
dependencyValue = it
}
val testLoop = TestLoop(this, dependency)
testLoop.update(1)
Assert.assertEquals(1, dependencyValue)
}
So I have to pass separate
val childScope = this + Job(coroutineContext.job)
to the TestLoopAdam Powell
01/14/2022, 4:31 PMTestLoop
expose a method to do this. TestLoop
as written always leaks itself via that actor since it has no public way to close.bezrukov
01/14/2022, 5:20 PM.cancel()
fun)
I think I do not agree, TestLoop doesn't live longer than the provided scope. It basically uses scope's cancellation as a way to close itself, which is a pretty common coroutines-usecase. I understand theas written always leaks itself via that actor since it has no public way to close.TestLoop
runScope
design decision though, so what I would love to see is something like TestScope.cancelAndComplete
that can be explicitly called once you finished your tests:
fun test() = runTest(..) {
val testLoop = TestLoop(this, dependency)
.... do some checks
cancelAndComplete()
}