Paul Woitaschek
03/24/2022, 6:36 PM@Test
fun test() = runTest {
repeat(5) {
launch {
println("enter")
Thread.sleep(Random.nextLong(10))
println("exit")
}
}
println("start yield")
yield()
}
Will print:
start yield
enter
exit
enter
exit
...
Is there any way to use runTest and having parallelism?Paul Woitaschek
03/24/2022, 6:37 PMGurpreetSK
03/25/2022, 5:41 AMPaul Woitaschek
03/25/2022, 7:22 AMPaul Woitaschek
03/25/2022, 7:23 AMDmitry Khalanskiy [JB]
03/25/2022, 7:27 AMrunTest
uses a single-threaded dispatcher. It still allows concurrency, so if you use delay
in your example, you'll see interleavings. However, there's no parallelism, as no additional threads are created.
We do consider adding multi-threaded test dispatchers, but didn't collect enough use cases yet: typically, code that relies on coroutines doesn't have complex parallel interactions, so there's not much demand.Paul Woitaschek
03/25/2022, 7:28 AMDmitry Khalanskiy [JB]
03/25/2022, 7:30 AMPaul Woitaschek
03/25/2022, 7:32 AMDmitry Khalanskiy [JB]
03/25/2022, 7:33 AMPaul Woitaschek
03/25/2022, 7:35 AMPaul Woitaschek
03/25/2022, 7:35 AMPaul Woitaschek
03/25/2022, 7:36 AMPaul Woitaschek
03/25/2022, 7:36 AMPaul Woitaschek
03/25/2022, 7:36 AMDmitry Khalanskiy [JB]
03/25/2022, 7:39 AMioContext
be some multithreaded dispatcher, like <http://Dispatchers.IO|Dispatchers.IO>
. I don't see any calls to delay
, so the delay-skipping behavior of the test dispatcher is not needed. runTest
supports waiting for computations forked off to other dispatchers.Dmitry Khalanskiy [JB]
03/25/2022, 7:42 AMdelay
so that the code that should execute in parallel has control of the test thread.Paul Woitaschek
03/25/2022, 7:44 AMPaul Woitaschek
03/25/2022, 7:44 AMPaul Woitaschek
03/25/2022, 7:49 AMallHeaders
the database has no values yetDmitry Khalanskiy [JB]
03/25/2022, 7:52 AMrunCurrent
is capable of, but only because the tests are single-threaded and runCurrent
blocks trying to execute the tasks. This is something that we'll have to carefully consider when designing multithreaded tests, this feels fairly common.Dmitry Khalanskiy [JB]
03/25/2022, 7:55 AM.age(
and .appVersion(
functions also accepting a function that they should run on completion (to notify that the update finished), the test could be fixed by doing something like
val deferred1 = CompletableDeferred<Unit>()
val deferred2 = CompletableDeferred<Unit>()
<http://updateUserProperties.app|updateUserProperties.app>(42) { deferred1.complete() }
updateUserProperties.appVersion("version") { deferred2.complete() }
deferred1.await()
deferred2.await()
However, adding this end-of-operation notification capability only for tests may be too much.Dmitry Khalanskiy [JB]
03/25/2022, 7:58 AMlaunch {
delay(Random.nextLong(10))
actuallyUpdate(age = 42)
}
launch {
delay(Random.nextLong(10))
actuallyUpdate(version = "version")
}
Here, the second actuallyUpdate
will run before the first one nearly 50% of the time.Paul Woitaschek
03/25/2022, 8:02 AMPaul Woitaschek
03/25/2022, 8:03 AMDmitry Khalanskiy [JB]
03/25/2022, 8:09 AMDmitry Khalanskiy [JB]
03/25/2022, 8:10 AMDmitry Khalanskiy [JB]
03/25/2022, 8:15 AMbuddyCount(
etc don't return anything. What if they returned the Job
?Guilherme Almeida
03/25/2022, 9:04 AMrunBlocking(<http://Dispatchers.IO|Dispatchers.IO>)
right? Since the core of the test runs on a separate context we are barely using the delay skipping mechanism from the TestScope and the code looks a bit more confusing having the custom job 🤔
But I do think there is a space for a parallelism capable runTest 🤞hfhbd
03/25/2022, 9:11 AMPaul Woitaschek
03/25/2022, 9:29 AMPaul Woitaschek
03/25/2022, 9:31 AMPaul Woitaschek
03/25/2022, 9:31 AM