Skip to main content

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.

What you need
  • The karate-async-<version>.jar — the engine build that carries the async protocols. Download it from the karate-addons releases.
  • A license with the grpc entitlement, at .karate/karate.lic in your project (or the KARATE_LICENSE_TEXT environment 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:

karate-boot.js
// 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).

note

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:

checks/hello.feature
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):

KeyMeans
hostserver host
portserver port
protodefault .proto file for every session
protoRootslist 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):

KeyMeans
host, portthe target, when it differs from the boot default
proto, protoRootsthe .proto file and its import roots
service, methodwhich RPC to call
send(msg)send one request message (JSON)
streamtrue 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
counthow 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
filtera JS function — keep only the responses it returns true for
metadatarequest metadata (a map)
metadataResponsethe response metadata, readable after the call
status, statusDetailsgRPC status name, and the rich-error details if any
trustCert, clientCert, clientKeyper-session TLS overrides
configbulk-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.

Part of Karate Agent

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