Skip to content

Spring Boot

The most complete JVM starter in Stove. It starts the real Spring Boot application, supports bridge() for DI access, and has the largest example set.

Open Spring + Postgres + Kafka in wizard

Three pieces 1) Add stove-spring. 2) Extract run(args) from your main. 3) Register systems first, then pass the runner to springBoot(runner = ...).

Anatomy of a Spring Boot setup

springBoot( 1 runner = { params -> 2 com.app.run(params) { addTestDependencies { 3 bean(isPrimary = true) bean { StoveSerde.jackson.anyByteArraySerde() } } } }, withParameters = listOf( 4 "server.port=8080", "logging.level.root=warn" ) )
1springBoot { } registers Spring as the AUT runner inside Stove().with { }.
2runner calls your extracted run(args). Same path production uses.
3addTestDependencies registers test-only beans (Kafka interceptor, custom serde). Use addTestDependencies4x for Spring Boot 4.x.
4withParameters adds static startup properties. Runtime values from systems still come from each system's configureExposedConfiguration.

Setup in five lines

dependencies {
    testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
    testImplementation("com.trendyol:stove")
    testImplementation("com.trendyol:stove-spring")
    testImplementation("com.trendyol:stove-extensions-kotest")  // or -junit
}

Extract run from main:

@SpringBootApplication
class ExampleApp

fun main(args: Array<String>) = run(args)

fun run(
  args: Array<String>,
  init: SpringApplication.() -> Unit = {}
): ConfigurableApplicationContext = runApplication<ExampleApp>(*args, init = init)

Minimal Stove().with { }:

Stove().with {
    httpClient { HttpClientSystemOptions(baseUrl = "http://localhost:8080") }
    springBoot(
        runner = { params -> com.app.run(params) },
        withParameters = listOf("server.port=8080")
    )
}.run()

Spring Boot 4.x

The bean registration API renamed. Swap addTestDependencies for addTestDependencies4x and bean for registerBean:

springBoot(
  runner = { params ->
    runApplication<MyApp>(*params) {
      addTestDependencies4x {
        registerBean<TestSystemKafkaInterceptor<*, *>>(primary = true)
        registerBean { StoveSerde.jackson.anyByteArraySerde(yourObjectMapper()) }
      }
    }
  }
)

Full bean registration details: Kafka, Bridge.

What you get

  • ✅ Real Spring Boot startup, real @Bean graph
  • ✅ bridge(). using<MyRepository> { ... } reads/writes via real DI
  • ✅ Component access to all Stove systems
  • ✅ Test-only bean registration for Kafka interceptors, custom serde, clocks, or fakes

Common pitfalls

Custom ObjectMapper?

Pass the same instance to Stove's serde. Otherwise dates and enums silently drift. See Best Practices · Serialization.

App reads spring.kafka.* properties but Stove injects kafka.*?

configureExposedConfiguration must produce the property names your app already reads. Check your KafkaProperties and mirror those keys.

Bean registration races

Use isPrimary = true (or 4.x primary = true) when overriding a production-registered bean.

Examples