gRPC Testing
Karate tests gRPC services through the commercial grpc extension. You point it at your .proto files directly — no code generation, no Maven or Gradle project. Checks are ordinary Karate feature files, so JSON assertions, match, data-driven rows and the HTML report all work unchanged, and you can mix HTTP and gRPC calls in one scenario.
- The
karate-async-<version>.jar— the engine build that carries the async protocols. Download it from the karate-addons releases. - A license with the
grpcentitlement, at.karate/karate.licin your project (or theKARATE_LICENSE_TEXTenvironment variable). - JDK 21+.
Activate the extension
A project is just a folder: your .proto files, your checks, and a karate-boot.js at the root. The boot file activates gRPC support and sets suite-wide defaults once:
// Activates gRPC support and sets the endpoint once, for the whole suite.
// Every karate.channel('grpc') session starts from these defaults; a session can override any of them.
var grpc = boot.ext('grpc');
grpc.host = boot.sysprop('grpc.host', 'localhost');
grpc.port = boot.sysprop('grpc.port', '50051');
// Import-resolution roots for the .proto — '/' is the project root, so a proto that
// imports another resolves from here.
grpc.protoRoots = ['/'];
boot.sysprop(name, default) reads a system property, so CI can re-point the suite without editing anything: java -Dgrpc.host=grpc.internal -jar karate-async-<version>.jar checks (the -D must come before -jar — that is where the JVM reads it).
boot.ext(...) is a load-time construct — it exists only inside karate-boot.js. See Extensions for how boot files work.
Write a check
A session is created with karate.channel('grpc'). Set the proto, service and method, send a message, and assert on the response as plain JSON:
Feature: gRPC checks
Background:
* def session = karate.channel('grpc')
* session.proto = '/proto/hello.proto'
* session.service = 'HelloService'
Scenario: unary — one request, one response
* session.method = 'Hello'
* session.send({ name: 'John' })
* match session.pop() == { message: 'hello John' }
Scenario: server streaming — collect a known number of responses
* session.method = 'LotsOfReplies'
* session.count = 3
* session.send({ name: 'John' })
* match session.collect() == [{ message: 'hello John 1' }, { message: 'hello John 2' }, { message: 'hello John 3' }]
Scenario: client streaming — many requests, one response
* session.method = 'LotsOfGreetings'
* session.stream = true
* session.send({ name: 'John' })
* session.send({ name: 'Smith' })
* session.flush()
* match session.pop() == { message: 'hello [John, Smith]' }
Scenario: bidirectional streaming — a response per request on one open stream
* session.method = 'BidiHello'
* session.stream = true
* session.count = 3
* session.send({ name: 'John' })
* session.send({ name: 'Smith' })
* session.send({ name: 'Jane' })
* match session.collect() ==
"""
[
{ message: 'hello [John]' },
{ message: 'hello [John, Smith]' },
{ message: 'hello [John, Smith, Jane]' }
]
"""
Run it: java -jar karate-async-<version>.jar checks — the HTML report lands in target/karate-reports/.
Metadata and errors
Scenario: request metadata, and reading the response metadata back
* session.metadata = { authorization: 'secret' }
* session.method = 'Hello'
* session.send({ name: 'John' })
* match session.pop() == { message: 'hello John' }
* match session.metadataResponse contains { 'authorization-response': 'secret-response' }
Scenario: the error path — an empty name is rejected
* session.method = 'Hello'
* session.send({ name: '' })
* match session.collect() == []
* match session.status == 'INVALID_ARGUMENT'
session.status is the gRPC status name ('OK' when the call succeeded). session.statusDetails carries the google.rpc.Status rich-error model when the server sends one, else null.
Configuration reference
Boot-level keys (on boot.ext('grpc') — suite-wide defaults):
| Key | Means |
|---|---|
host | server host |
port | server port |
proto | default .proto file for every session |
protoRoots | list of directories that proto import statements resolve against |
tls | { trustCert, clientCert, clientKey } — see TLS and mTLS |
Session keys (on karate.channel('grpc') — each overrides the boot default):
| Key | Means |
|---|---|
host, port | the target, when it differs from the boot default |
proto, protoRoots | the .proto file and its import roots |
service, method | which RPC to call |
send(msg) | send one request message (JSON) |
stream | true for client-streaming and bidirectional calls — keeps the request stream open across send() calls |
flush() | close the request stream; needed when the server waits for the client stream to complete |
count | how many responses collect() waits for (default 1) |
pop() | take the next response |
collect() | take the responses — blocks until count have arrived, so set count first |
filter | a JS function — keep only the responses it returns true for |
metadata | request metadata (a map) |
metadataResponse | the response metadata, readable after the call |
status, statusDetails | gRPC status name, and the rich-error details if any |
trustCert, clientCert, clientKey | per-session TLS overrides |
config | bulk-set: session.config = { host: 'localhost', port: 50051, proto: '/proto/hello.proto' } |
TLS and mTLS
Set once for the suite in karate-boot.js:
var grpc = boot.ext('grpc');
grpc.tls = {
trustCert: '/certs/ca.pem', // verify the server
clientCert: '/certs/client.pem', // present a client cert (mTLS)
clientKey: '/certs/client-key.pem'
};
…or per session, with session.trustCert / session.clientCert / session.clientKey.
Path references
A leading / always means the project root, never the file system root — so /proto/hello.proto is portable across machines and CI. For a real host path (a mounted secret, /etc/ssl/...), use file:/....
Runnable example
A complete, CI-verified project — proto, demo server, checks and boot file — is at karate-agent-examples/grpc, with its live report. If a snippet on this page ever disagrees with that kit, the kit is the one gated by CI.
The karate-async jar is a build of Karate Agent — the AI-native engine that adds a served console, API coverage and requirements traceability over these same feature files, and teaches AI assistants the whole surface at runtime. See the enterprise overview.
See Also
- Kafka Testing — the same channel model over Kafka topics
- WebSocket Testing — the commercial WebSocket extension
- Extensions — how
karate-boot.jsandboot.ext(...)work - Enterprise Extensions — the commercial module roster