Systems¶
Stove is pluggable. Each dependency, test-side client, mock, and observability surface is a separate system module. AUT runners are registered separately to start or target the application under test. Add only what your tests need. Default mode uses Testcontainers; switch to Provided Instances when the infrastructure already exists or Docker is unavailable.
Most teams start small Two to four systems usually gets a useful first suite: one driver such as HTTP or gRPC, and the stateful systems the use case touches. Then register one AUT runner. Add tracing and dashboard when failure diagnosis needs more than console output.
Pick a starting set¶
| You're testing | Add these |
|---|---|
| HTTP API + SQL | stove-http + stove-postgres (or -mysql) |
| Event-driven service | stove-kafka + your DB + stove-tracing |
| Service calling external API | stove-http + stove-wiremock |
| gRPC service | stove-grpc + stove-grpc-mock |
| Stateful service with caching | your DB + stove-redis |
| Already-running service (any language) | stove-http + any provided dependencies + providedApplication() |
Wizard composes this for you: open with Postgres + Kafka + WireMock.
Catalog¶
🗄 Databases
📨 Messaging
🌐 Network
🪞 Mocks
📈 Observability
🔌 Integration
Every system shares the same shape¶
Once you configure one system, the lifecycle is the same for the rest.
.provided(...) for existing infrastructure.Container mode vs Provided Instances¶
Container mode (default)
Stove starts Testcontainers. Best for local development and CI runners with Docker.
See Provided Instances for prefixing strategies that prevent collisions on shared infra.
Migrations and cleanup¶
Databases and other stateful systems support migrations and per-system cleanup hooks. Migrations run during suite startup before the application receives dependency configuration; cleanup runs when Stove stops at suite teardown.
class CreateTablesMigration : DatabaseMigration<PostgresSqlMigrationContext> {
override val order: Int = 1
override suspend fun execute(connection: PostgresSqlMigrationContext) {
connection.operations.execute("CREATE TABLE orders ...")
}
}
postgresql {
PostgresqlOptions(
cleanup = { ops -> ops.execute("DELETE FROM orders WHERE test = true") },
configureExposedConfiguration = { cfg -> listOf(...) }
).migrations { register<CreateTablesMigration>() }
}
Full-stack test, all systems at once¶
test("order flows across HTTP, DB, Kafka, ES, Redis") {
stove {
val orderId = UUID.randomUUID().toString()
wiremock {
mockPost("/payments", 200, PaymentResult(success = true).some())
}
http {
postAndExpectBody<OrderResponse>(
"/orders", body = CreateOrderRequest(orderId).some()
) { it.status shouldBe 201 }
}
couchbase {
shouldGet<Order>("orders", orderId) { it.status shouldBe "CREATED" }
}
kafka {
shouldBePublished<OrderCreatedEvent> { actual.orderId == orderId }
}
elasticsearch {
shouldGet<Order>(index = "orders", key = orderId) {
it.status shouldBe "CREATED"
}
}
redis {
client().connect().sync().get("order:$orderId") shouldNotBe null
}
}
}
For a full annotated walkthrough of this pattern, see the order placement recipe.
Where to next¶
-
Compose your stack. Setup Wizard
-
End-to-end scenarios. Recipes
-
When a test fails. Observability story
-
Per-framework guides. Frameworks