Mike Dawson
09/22/2023, 5:26 PMJoffrey
09/22/2023, 5:38 PMMike Dawson
09/22/2023, 5:46 PMMike Dawson
09/22/2023, 5:51 PMMike Dawson
09/22/2023, 5:56 PMJoffrey
09/22/2023, 6:01 PMMike Dawson
09/22/2023, 6:11 PMkevin.cianfarini
09/22/2023, 6:36 PM@Test fun foo() = runTest {
// some code
withVirtualTime { // this: VirtualTimeManager
this.advanceTimeBy(...)
}
// more code
}
kevin.cianfarini
09/22/2023, 6:39 PM@Test fun foo() = runTest {
// some code
withVirtualTime { // this: VirtualTimeManager
this.advanceTimeBy(...)
}
withDelaySkipping {
// code that skips delays and advances time for you automatically.
}
// more code
}
Joffrey
09/22/2023, 6:40 PMrunBlockingTest
was JVM only, so the difference with runBlocking
mostly lied on test-specific sugar like virtual time. However, when it became multiplatform, it became the default (only?) way to test coroutines on other platforms (even when you don't need/want virtual time), but the default behaviour wasn't changed.
I also believe we shouldn't have virtual time by default, but that's a debate for the issue itself.Mike Dawson
09/22/2023, 6:47 PMkevin.cianfarini
09/22/2023, 6:49 PMkevin.cianfarini
09/22/2023, 6:50 PMbackgroundScope
which are convenientMike Dawson
09/22/2023, 6:51 PMMike Dawson
09/22/2023, 6:54 PMJoffrey
09/22/2023, 6:55 PMJoffrey
09/22/2023, 6:56 PMMike Dawson
09/22/2023, 7:08 PMJoffrey
09/22/2023, 7:10 PMDispatchers.Default
to fix the problem, though. Even Dispatchers.Main
should work too I believe (I you do need Main), so long as you don't use the test dispatcher anymoreKirill Zhukov
09/23/2023, 3:57 AMJoffrey
09/23/2023, 8:40 AMdelay()
just moves the virtual clock. It's not really that it is skipped per se, but rather that the time it took is virtual. If you have 2 concurrent pieces of code, one with and one without delay, the one without will run first. Using advanceTimeBy
and similar is just a way to move the virtual time in places without delay. Do you have an example in mind of something you cannot do right now and which would be enabled by your suggestion?Dmitry Khalanskiy [JB]
10/05/2023, 8:31 AMThere are a lot of times when a timeout is needed in the real code e.g. network calls, database operations, etc.I answered your point under the issue, but will repeat it here: yes, that's true, sometimes tests do need to do real network calls and database operations (though usually these are replaced with mocks).
I tried using withContext(Dispatchers.Default.limitedParallelism(1)) - but when something happens inside the main context, it seems like that doesn't take effect.Yes, in
Dispatchers.Main
, time will get skipped if you call Dispatchers.setMain
with a test dispatcher. This is never a problem, because you must never-ever run blocking code in Dispatchers.Main
. Not network calls, not database access, not even computations so long you have to wrap them in a withTimeout
. The main thread is the one drawing the UI, and if you run such long operations on the main thread, the UI will freeze. On Android, it will manifest in ANRs: https://developer.android.com/topic/performance/vitals/anr
I'm just a bit confused because my understanding was that runTest was a general purpose multiplatform wrapper to test anything that involves coroutines (given that on JVM it needs to use runblocking and on Javascript the test needs to return a promise).Yep, that's more or less the case. I wouldn't call it "general purpose" as it's heavily opinionated by design. In some aspects, it's for the reasons of historical evolution, but generally, it's because it's a test framework, not a test library that would provide you with a bunch of functions for constructing your own test environment.
runTest
is supposed to "just work" for the vast majority of cases.
Is it more intended for testing coroutine internals?Nope, it's not suitable for that. It's too inflexible for foundational code. The idea is that the people actually writing foundational coroutines code understand the internals well enough to be able to write their own testing infrastructure.
``` withVirtualTime { // this: VirtualTimeManager
this.advanceTimeBy(...)
} ```The more I'm working on the test framework, the harder it is for me to understand the need to have
advanceTimeBy
or advanceUntilIdle
. I have not yet seen a class of real-life tests where using these instead of just relying on delay-skipping results in cleaner code. If someone here is a heavy user of advanceTimeBy
, please send me these tests, and we'll see if your tests can't actually be rewritten more clearly.
delay skipping and manual time control, which myself and others have found confusingYeah, manual time control is tricky. I'd advise to avoid it.
I also believe we shouldn't have virtual time by default, but that's a debate for the issue itself.Yes. If you have some questions you want clarified, feel free to ask them here so that we don't create extra noise under the issue itself, but if you have a point regarding it that you think wasn't raised before, please post it there.
EvenIf there are any reasons not to use test dispatchers forshould work too I believe (I you do need Main), so long as you don't use the test dispatcher anymoreDispatchers.Main
Dispatchers.Main
, please also share them.