Skip to content

Polyglot

Stove's testing model is not limited to JVM applications. If an application can be started as a process, a container, or an already-running endpoint, Stove can drive it through external systems such as HTTP, gRPC, databases, Kafka, WireMock, and tracing. Go, Python, Rust, Node, and .NET follow the same runtime pattern; the language-specific work is reading configuration and exporting telemetry.

Same DSL, different runner stove { http { } postgresql { } kafka { } tracing { } } keeps the same shape for registered systems. The application-under-test (AUT) runner changes to processApp(), goApp(), containerApp(), or providedApplication(). Process and container runners map system configuration into env vars or CLI args before startup. providedApplication() targets an app that is already running and externally configured; Stove checks readiness and runs assertions against configured endpoints and provided infrastructure. Bridge is unavailable because the app runs outside the test JVM.

Two ways to run the app

🏃 stove-process

Run a host binary (goApp() / processApp()). Fastest inner loop when local runtime drift is acceptable.

  • Quick to iterate
  • Zero infra beyond your compiler
  • Approximate production parity (host runtime)
  • Best for dev + smoke tests

🐳 stove-container

Run any Docker image (containerApp()). Stronger parity with the artifact you ship.

  • Production-image parity
  • Image build per run
  • Best for pre-merge + release validation
  • Language-agnostic

A common pattern: e2eTest (process) for dev, e2eTest-container for CI. Same Kotlin tests, same StoveConfig, branched on -Dgo.aut.mode=process|container.

How it works

graph LR
    S[Stove Test<br/>Kotlin] -->|starts containers| PG[(PostgreSQL)]
    S -->|starts containers| K[(Kafka)]
    S -->|starts OTLP receiver| T[Tracing]
    S -->|process or container| APP[Your App<br/>Go · Python · Rust · ...]
    APP -->|connects| PG
    APP -->|connects| K
    APP -->|exports spans| T
    S -->|HTTP / gRPC asserts| APP
    S -->|DB asserts| PG
    S -->|trace asserts| T

For process and container runners, Stove launches infrastructure, maps connection details into environment variables or CLI arguments, waits for readiness, and runs the standard DSL against the AUT. With providedApplication(), Stove does not start the app or inject configuration; the app must already be configured to reach the same provided infrastructure.

Language requirements

Any language can work if the AUT can:

  1. Read env vars (or CLI args) for DB URLs, ports, credentials
  2. Expose readiness. HTTP /health (preferred), TCP probe, custom probe, fixed delay
  3. Handle SIGTERM. Needed for clean teardown and, for Go, integration coverage flush

Languages we walk through

GoFirst-class

HTTP · Postgres · Kafka (sarama / franz-go / segmentio) · OTel · Dashboard · MCP · integration coverage.

Python, Rust, Node, and .NET follow the same shape. Pick processApp() or containerApp(), map the configuration your app needs, and use that language's OpenTelemetry SDK if you want trace assertions. Open an issue if you want a dedicated walkthrough.

Process vs container at a glance

Concern stove-process stove-container
Starter goApp() / processApp() containerApp()
AUT artifact Host binary Docker image
Iteration speed Fast (compile + run) Slower (image build)
Production parity Approximate Exact
CI fit Smoke / inner loop Pre-merge / release
Networking Loopback Host network or port binding
Filesystem isolation Host filesystem Container layer + bind mounts
Common pitfalls Runtime drift hidden Network mode + port wiring

vs JVM apps

Concern JVM app Non-JVM app
AUT startup springBoot(), ktor(), ... goApp() / processApp() / containerApp()
Config JVM properties envMapper / argsMapper
Infra Same (postgresql { }, kafka { }, ...) Same
Test DSL Same Same
Tracing OTel Java Agent (auto) OTel SDK for your language
Dashboard / MCP Same Same
Bridge using<T> { } ✗ (separate process / container)

The pattern

1. Wire the AUT

// Process mode
goApp(
  target = ProcessTarget.Server(port = 8090, portEnvVar = "APP_PORT"),
  envProvider = envMapper { /* ... */ }
)

// Container mode
containerApp(
  image = "my-app:local",
  target = ContainerTarget.Server(hostPort = 8090, internalPort = 8090, portEnvVar = "APP_PORT"),
  envProvider = envMapper { /* ... */ },
  configureContainer = { withNetworkMode("host") }
)

2. Instrument with OpenTelemetry

Enable the stoveTracing Gradle plugin. It boots the OTLP gRPC receiver, picks a free port, and exposes it to your tests + AUT via env vars:

plugins {
    id("com.trendyol.stove.tracing") version "$stoveVersion"
}

stoveTracing {
    serviceName.set("my-service")
    testTaskNames.set(listOf("e2eTest"))   // and "e2eTest-container", etc.
}

Then instrument your app with your language's OTel SDK. Stove correlates spans back to the test via W3C traceparent. JVM apps can use the Java agent; non-JVM apps need explicit SDK instrumentation and must read the OTLP endpoint from the standard env vars.

3. Write tests with the standard DSL

http { }, postgresql { }, kafka { }, and tracing { } use the same Stove test DSL as JVM tests, as long as the corresponding systems are registered and the AUT is configured to talk to them. Dashboard and MCP are observability and access surfaces; they record or expose run evidence but are not assertion blocks.

What you can't do

  • ❌ No bridge() / using<T> { }. Different process / container.

External-surface assertions still work: HTTP/gRPC, database queries, WireMock, and tracing. Kafka assertions such as shouldBePublished and shouldBeConsumed require a Stove Kafka bridge or equivalent client instrumentation in the non-JVM app. Dashboard and MCP can expose the resulting evidence when enabled.

Kafka assertions for non-JVM apps

Stove ships bridge libraries to expose shouldBeConsumed / shouldBePublished for non-JVM apps. The stove-kafka Go library supports IBM/sarama (interceptors), twmb/franz-go (hooks), and segmentio/kafka-go (helpers). The library-agnostic core lets you wire any other client (e.g. confluent-kafka-go).

Next