Benoît Liessens
04/09/2025, 5:24 PMclass ApplicationTest {
@Test
fun testRoot() = testApplication {
application {
module()
}
val response = client.get("/")
assertEquals(HttpStatusCode.OK, response.status)
assertEquals("Hello, world!", response.bodyAsText())
}
}
Am I correct to state that above KTor app (module) isn’t started until the client GET request to “/” is performed?
Now the module I want to test does not have HTTP endpoints at all. The module start a set of background coroutines. In order to test the module I find myself doing an explicit call to startApplication()
:
class ApplicationTest {
@Test
fun testRoot() = testApplication {
environment {
// load particular config
}
application {
myModuleWithCoroutines()
}
startApplication()
// assert outcome of coroutines
}
}
Above works, but I have no means to stop the KTor module. There is no stopApplication()
counterpart which prevents me to have a 2nd test (method) with different configuration. What is the advised approach here to start and stop my non-HTTP KTor module?
ThanksAleksei Tirman [JB]
04/10/2025, 7:34 AMAm I correct to state that above KTor app (module) isn’t started until the client GET request to “/” is performed?That's correct.
What is the advised approach here to start and stop my non-HTTP KTor module?Unfortunately, the
stop
method of the TestApplication
class cannot be called by the users due to visibility restrictions. The only way I found is to get the application
property via reflection:
// ...
startApplication()
// assert outcome of coroutines
val appProp = ApplicationTestBuilder::class.memberProperties.first { it.name == "application" }
val app = appProp.get(this) as TestApplication?
app?.stop()
Lidonis Calhau
04/10/2025, 11:46 AMBenoît Liessens
04/10/2025, 1:09 PMBenoît Liessens
04/22/2025, 9:09 PMtestApplication {}
did does that just fine.
What I was missing in my KTor module, was proper logic to close (cancel) the coroutines. Adding this was enough:
monitor.subscrbe(ApplicationStopping) {
// cancel whatever coroutines was launched by the KTor module.
}
Additionally, also the supervisorScope{}
created in my KTor module had to be cancelled.