junit5 icon indicating copy to clipboard operation
junit5 copied to clipboard

Kotlin `suspend` test should use `runTest`.

Open JavierSegoviaCordoba opened this issue 6 months ago • 8 comments

Context, this GitHub comment and the ones below it: https://github.com/junit-team/junit-framework/issues/1914#issuecomment-2893644101

Furthermore, if suspend tests use runTest, it would be amazing to support specifying a dispatcher via an annotation:

@TestDispatcher(FooDispatcher::class)
class BarTest {

    ...
}

I find it weird to need to replace the dispatcher only on a single test instead of the whole class. @TestDispatcher(FooDispatcher::class) should work if it is also annotating a test method for that use case.

JavierSegoviaCordoba avatar Jul 15 '25 11:07 JavierSegoviaCordoba

Most built-in dispatchers do not exposed as a type:

https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/src/Dispatchers.kt#L14

DefaultScheduler, DefaultIoScheduler, Unconfined - internal objects Main - property StandardTestDispatcher, UnconfinedTestDispatcher - functions

So it's not realistic to support FooDispatcher::class, unless it's some factory that returns dispatcher. So then it would be nice to have built-in factory implementations for well-known dispatchers.

IRus avatar Jul 15 '25 12:07 IRus

More generic feature would be supporting injection arbitrary CoroutineScope, since there is no benefit for tests to inject StandardTestDispatcher but not be able to access TestScope.

And then, if you look at amount of boilerplate that passing TestScope via parameter of context parameter requires, it's less obvious why one will not just write:

@Test
fun testExampleBackgroundJob() = runTest {
    ...
}

and prefer this:

@Test
suspend fun testExampleBackgroundJob(testScope: TestScope)  {
}

// or

@Test
context(testScope: TestScope)
suspend fun testExampleBackgroundJob()  {
}

// this is kinda ok, but again just more boilerplate than runTest
@Test
suspend fun TestScope.testExampleBackgroundJob()  {
}

IRus avatar Jul 15 '25 12:07 IRus

I don't know how JUnit works, but just adding the annotation should add the TestScope automatically as some kind of receiver, probably as a context parameter as it is the future of Kotlin, even if the user has not explicitly written it.

JavierSegoviaCordoba avatar Jul 15 '25 13:07 JavierSegoviaCordoba

I agree. My hack https://gist.github.com/ephemient/01d6e5766e6f8ea02839b4d7c3f94e55 supported test methods receiving TestScope as a receiver or other parameter, and context parameters would make sense too.

ephemient avatar Jul 15 '25 13:07 ephemient

And then, if you look at amount of boilerplate that passing TestScope via parameter of context parameter requires, it's less obvious why one will not just write:

@Test
fun testExampleBackgroundJob() = runTest {
    ...
}

This already works today, doesn't it? If so, I think we should document is as an alternative to suspend functions (or using runBlocking) directly. If anyone needs a special dispatcher, I think they should set this up themselves in the test method body. WDYT?

marcphilipp avatar Jul 17 '25 10:07 marcphilipp

@marcphilipp yes, I think using runTest directly make more sense and can be just documented. It's part of kotlinx-coroutines-test and user can use any version, so junit do not need to add it to classpath or require it in runtime.

Not sure about use-case for injecting dispatcher, I don't have any.

IRus avatar Jul 17 '25 12:07 IRus

If you would like us to be able to process this issue, please provide the requested information. If the information is not provided within the next 3 weeks, we will be unable to proceed and this issue will be closed.

github-actions[bot] avatar Aug 01 '25 02:08 github-actions[bot]

Closing due to lack of requested feedback. If you would like to proceed with your contribution, please provide the requested information and we will re-open this issue.

github-actions[bot] avatar Aug 23 '25 02:08 github-actions[bot]