gRPC Client¶
Call your app's gRPC services from tests. Supports Wire, grpc-kotlin, and custom client factories. Unary, server-stream, client-stream, bidi.
gRPC client — wizard-synced snippet
Gradle
Stove configuration
Test DSL
In 30 seconds
Register grpc { GrpcSystemOptions(host = "localhost", port = 9090) }. Use wireClient<T>() (Wire) or channel<T>() (gRPC-kotlin). For raw access, rawChannel { channel -> }. Streaming via coroutines: serverStream, clientStream, bidiStream.
Configure¶
Stove().with {
grpc {
GrpcSystemOptions(
host = "localhost",
port = 9090,
usePlaintext = true,
timeout = 30.seconds,
interceptors = listOf(/* ClientInterceptor */),
metadata = Metadata().apply {
put(Metadata.Key.of("x-source", Metadata.ASCII_STRING_MARSHALLER), "stove")
}
)
}
}.run()
| Field | Use |
|---|---|
host, port |
Where your gRPC server listens |
usePlaintext |
true for local tests; flip when testing TLS |
timeout |
Per-call deadline |
interceptors |
ClientInterceptor chain |
metadata |
Default per-call metadata (auth, tracing, ...) |
createChannel |
Custom ManagedChannel factory |
createWireClient |
Custom Wire client factory |
DSL¶
Wire client¶
stove {
grpc {
wireClient<OrderServiceClient>().createOrder(
OrderRequest(userId = "u1", amount = 99.99)
).status shouldBe OrderStatus.CREATED
}
}
grpc-kotlin channel¶
stove {
grpc {
val stub = channel<OrderServiceGrpcKt.OrderServiceCoroutineStub>()
stub.createOrder(orderRequest).status shouldBe "CREATED"
}
}
Per-call metadata override¶
stove {
grpc {
withEndpoint(::OrderServiceClient) {
metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), "Bearer x")
createOrder(orderRequest)
}
}
}
Raw channel access¶
stove {
grpc {
rawChannel { channel ->
val stub = MyServiceGrpc.newBlockingStub(channel)
stub.something()
}
}
}
Streaming¶
stove {
grpc {
// server stream
serverStream(::OrderServiceClient) {
val flow = streamOrders(StreamRequest(userId = "u1"))
flow.toList() shouldHaveSize 10
}
// client stream
clientStream(::OrderServiceClient) {
val reply = bulkCreate(flowOf(o1, o2, o3))
reply.created shouldBe 3
}
// bidi
bidiStream(::OrderServiceClient) {
val out = chat(flowOf("hi", "hello"))
out.toList().size shouldBe 2
}
}
}
Error handling¶
stove {
grpc {
shouldThrow<StatusException> {
wireClient<OrderServiceClient>().getOrder(OrderRequest(id = "missing"))
}.status.code shouldBe Status.Code.NOT_FOUND
}
}
Multiple gRPC services (keyed)¶
object Inventory : SystemKey
object Payments : SystemKey
Stove().with {
grpc(Inventory) { GrpcSystemOptions(host = "localhost", port = 9090) }
grpc(Payments) { GrpcSystemOptions(host = "localhost", port = 9091) }
}
stove {
grpc(Inventory) { wireClient<InventoryClient>().reserve(/* ... */) }
grpc(Payments) { wireClient<PaymentClient>().charge(/* ... */) }
}
See Multiple Systems.