Ktor¶
Stove starts your real Ktor server. It can resolve beans through Koin, Ktor-DI, or a custom resolver when you register bridge().
Open Ktor + Postgres in wizard
Two knobs
1) Your run(args, wait = false, ...) returns the started Application. 2) ktor(runner = ...) calls it after systems are ready. If you use bridge(), Stove auto-detects Koin vs Ktor-DI; custom containers plug in via a resolver lambda.
Anatomy¶
ktor { } registers Ktor as the AUT runner.runner calls your extracted run. Pass test modules / test deps here.shouldWait = false is critical. Stove keeps the suite alive itself.Setup¶
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
testImplementation("com.trendyol:stove")
testImplementation("com.trendyol:stove-ktor")
testImplementation("com.trendyol:stove-extensions-kotest") // or -junit
}
Extract run to accept test overrides:
fun main(args: Array<String>) = run(args, shouldWait = true).let { Unit }
fun run(
args: Array<String>,
shouldWait: Boolean = false,
testModules: List<Module> = emptyList()
): Application {
val cfg = loadConfiguration<AppConfiguration>(args)
return embeddedServer(Netty, port = cfg.port, host = "localhost") {
install(Koin) { modules(appModule, *testModules.toTypedArray()) }
configureRouting()
}.start(wait = shouldWait).application
}
fun main(args: Array<String>) = run(args, shouldWait = true).let { Unit }
fun run(
args: Array<String>,
shouldWait: Boolean = false,
testDependencies: (DependencyRegistrar.() -> Unit)? = null
): Application {
val cfg = loadConfiguration<AppConfiguration>(args)
return embeddedServer(Netty, port = cfg.port, host = "localhost") {
install(DI) {
dependencies {
provide<MyService> { MyServiceImpl() }
testDependencies?.invoke(this)
}
}
configureRouting()
}.start(wait = shouldWait).application
}
Minimal Stove().with { }:
Stove().with {
httpClient { HttpClientSystemOptions(baseUrl = "http://localhost:8080") }
ktor(
runner = { params -> run(params, shouldWait = false) },
withParameters = listOf("port=8080")
)
}.run()
Bridge. Automatic DI detection¶
| DI framework | Detection | Priority |
|---|---|---|
| Ktor-DI | dependencies { } block active |
Preferred when both present |
| Koin | install(Koin) { } active |
Used when Ktor-DI absent |
| Custom (Kodein, Dagger, etc.) | Manual resolver lambda | Explicit override |
Test overrides¶
Using Bridge in tests¶
stove {
using<UserService> {
findById(123).name shouldBe "John"
}
using<List<PaymentService>> {
forEach { it.validate() }
}
}
Full patterns (multi-bean access, value capture, generics): Bridge reference.
What you get¶
Real Netty server, real routing
bridge()for Koin and Ktor-DI when the corresponding plugin is installed normallyComposes with every Stove system
Hot-swap DI containers via custom resolver
Common pitfalls¶
shouldWait = true hangs the suite
Production main waits; tests must not. Always pass shouldWait = false from the runner.
DI not detected
Bridge looks for install(Koin) or install(DI). If you wrap them in feature plugins, expose a custom resolver.
Example¶
- ktor-example. Full stack with Koin + Postgres + Kafka