Lucas Kivi
09/13/2023, 10:23 PMrunTest
in coroutines 1.7.0
that are causing us some problems.. (see thread)Lucas Kivi
09/13/2023, 10:23 PMThread
’s uncaught exception handler for exceptions thrown in other `CoroutineScope`s like so:
fun <T : Throwable> assertThrowsInCoroutine(
expectedThrowable: Class<T>,
block: () -> Unit,
): T {
val originalHandler = Thread.getDefaultUncaughtExceptionHandler()
var throwable: Throwable? = null
Thread.setDefaultUncaughtExceptionHandler { _, t -> throwable = t }
try {
block()
} catch (t: Throwable) {
// Manually catch this here just in case the exception is not in a coroutine
throwable = t
}
Thread.setDefaultUncaughtExceptionHandler(originalHandler)
return assertThrows(expectedThrowable) {
throwable?.let { throw it }
}
}
private fun somethingThatBuildsACoroutineScopeAndThrows() {
CoroutineScope(Dispatchers.Unconfined).launch {
throw IllegalStateException("Something is happening")
}
}
@Test
fun `original test`() {
assertThrowsInCoroutine(IllegalStateException::class.java) {
somethingThatBuildsACoroutineScopeAndThrows()
}
}
However, as of 1.7.0
, this is broken. Now the exception goes uncaught and we see
org.opentest4j.AssertionFailedError: Expected java.lang.IllegalStateException to be thrown, but nothing was thrown.
…However, if we wrap our
somethingThatBuildsACoroutineScopeAndThrows()
in runTest
it succeeds!
@Test
fun `new test`() {
assertThrowsInCoroutine(IllegalStateException::class.java) {
runTest {
somethingThatBuildsACoroutineScopeAndThrows()
}
}
}
Does anyone know of a way to intercept this uncaught exception such that assertThrowsInCoroutine
does not need to contain runTest
?