Paul Woitaschek
07/01/2023, 7:46 PMkotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 10s, the test coroutine is not completing
They all have in common that they use mockk, so ByteBuddy - and that seems to just be expensive, especially on the first invocations.
I found this (closed issue)
https://github.com/Kotlin/kotlinx.coroutines/issues/3270
Did someone find a workaround here?Javier
07/01/2023, 8:52 PMrunTest
function. I had to change it due Windows CI was taking more than 10 secs on GitHub Actions.Paul Woitaschek
07/02/2023, 6:00 AMDmitry Khalanskiy [JB]
07/02/2023, 8:24 AMrunTest
.Paul Woitaschek
07/02/2023, 7:43 PMJavier
07/02/2023, 8:48 PMchristophsturm
07/03/2023, 7:32 AMPaul Woitaschek
07/03/2023, 7:34 AMchristophsturm
07/03/2023, 7:37 AMPaul Woitaschek
07/03/2023, 7:38 AMchristophsturm
07/03/2023, 7:39 AMPaul Woitaschek
07/03/2023, 7:39 AMPaul Woitaschek
07/03/2023, 7:39 AMchristophsturm
07/03/2023, 7:39 AMPaul Woitaschek
07/03/2023, 7:40 AMchristophsturm
07/03/2023, 7:40 AMPaul Woitaschek
07/03/2023, 7:40 AMchristophsturm
07/03/2023, 7:41 AMchristophsturm
07/03/2023, 7:42 AMPaul Woitaschek
07/03/2023, 7:42 AMchristophsturm
07/03/2023, 7:44 AMchristophsturm
07/03/2023, 8:38 AMDmitry Khalanskiy [JB]
07/03/2023, 10:14 AMBut actually I wonder: shouldn’t timeouts be the responsibility of the test framework?Ideally, maybe, but as far as I know, there's no cross-platform mechanism for this. When writing tests in common code, you have no way to indicate "This particular test may take a whole minute, whereas all other tests take ten seconds at most".
Everyone creates their own run test functions because busy and possibly slow CIs, as well as slow byte buddy making all tests flaky: This doesn’t make sense to me because of a huge overhead"Everyone" here is a bit misleading. The overwhelming majority of tests don't have this problem. 10 seconds is an enormous amount of time by computing standards.
Adding an option to make it globally configurable, i.e. through an env variable - could be a viable option.Agreed. If there's enough demand, we should look into it.
Revering the change - does make sense to me. I rather wait 50 seconds longer for a really stale test instead of finding out the CI failed again due to flakiness and restarting the tests a few times until they succeedThis only makes sense if the problems from having a 10-second default are worse than the problems from not having it, and in the issue you linked, there are some compelling arguments for this default. If it turns out that the default actually doesn't make sense for most use cases, then this change should be reverted, sure, but so far it's not at all clear.
Moving all mocking setup outside run test. That will only partly work because often times it’s the first invocation that’s slow. Also this forces users to workaround and split up their logic due to technical reasonsMaybe you have a case here. Depends on what the mocking code looks like. I imagined something like
@Test fun testObtainingWebpage() = runTest {
ensureStableConnection()
val result = getPage("<https://google.com/>")
assertIs<Success>(result)
assertEquals("Google", result.title)
}
@Test fun testTimingOut() = runTest {
ensureFlakyConnection()
val result = getPage("<https://google.com>")
assertIs<Failure>(result)
}
@BeforeTest fun mockConnection() {
// initialization here
}
So the code in runTest
roughly looks like some execution similar to what's going on in production, interspersed with checking the results and controlling the details of the execution context, and the code in BeforeTest
performs the initialization of the components needed for that. Maybe the typical usage of mocking is somehow different and doesn't map to this pattern, I don't really know.
Removing the timeout from runTest and instead adding a runTestWithTimeout function - also a viable option.This, too, depends on the use case that's most commonly needed. If it turns out that the overwhelming majority of people do have a test suite where most tests finish in under a second and for a test to take 10 seconds certainly means that the test hanged, then the right choice,
runTest
with a timeout, should be easily accessible.
My use case is similar in terms of, it is normal the test takes more time, it is using property testing, so it is running a lot of tests in oneIt's surprising to see that someone is using property testing with coroutines! I'd expect to see property tests of some well-defined pure code that performs a specific computation, but not property tests of concurrent procedures.
Paul Woitaschek
07/03/2023, 10:28 AM@Test
fun myMockingTest() {
measureTime {
mockk<UUID>()
}.also {
println("That took $it")
}
}
Takes 1.5 seconds on my M1 Max.
On a slower CI runner which is also possibly running multiple projects in parallel using containerization this is already getting flakyPaul Woitaschek
07/03/2023, 10:31 AMThat took 1.409419875s
That took 173.458us
That took 61.917us
That took 61.208us
That took 110.25us
That took 64.5us
That took 60.083us
That took 62.875us
That took 57.417us
That took 58us
Javier
07/03/2023, 10:38 AMsuspend
test, and the framework can adapt to it, allowing all features it has but on the coroutines world tooPaul Woitaschek
07/04/2023, 12:33 PMgildor
08/15/2023, 2:25 AMapart from that I think most people have a shared class that is imported in all test cases, and there i would put it.It's not true in my experience, there is just nothing to share in most cases, and a vast majority of projects on which I work don't have any base classes And it's even more rare recently with multi-module projects and composition over inheritance approach
gildor
08/15/2023, 2:31 AMmy solution is to not use mockk.It's not a general solution, is an attempt to have workaround for particular case
10 seconds is an enormous amount of time by computing standardsIt really depends what is going on and what kind project it is For example when sync of the project in IDEA takes 5-10 minutes, or when execution of hello world Kotlin script takes a couple of seconds thanks to classpath size and jvm warm up
gildor
08/15/2023, 2:41 AMchristophsturm
08/15/2023, 8:12 AMgildor
08/16/2023, 6:36 AM