Keploy logo
gRPC logo

Keploy as a gRPC testing framework

Keploy tests gRPC services by recording the HTTP/2 frames between client and server, capturing the serialised Protobuf messages and status codes exchanged, then replaying them during tests. There is no stub server to run and no mock service implementation to generate and maintain.

Generate gRPC tests free
keploy record -c "./orders-service"
18.4K+VS Code1.2M+300M+mocks created

What Keploy gives a gRPC team

Captured as HTTP/2 frames

Keploy reads the framed gRPC exchange beneath your generated client, recording the method path, request metadata, serialised messages, status code, and trailing metadata together.

  • Below grpc-java, grpc-go, grpc-js, grpcio
  • Serialised Protobuf captured verbatim
  • Status codes and trailers preserved
  • Streaming replays as an ordered sequence
The problem

Why gRPC integration tests slow teams down

The friction is rarely the assertions. It is everything around them — spinning up dependencies, keeping mocks honest, and repairing tests after every refactor.

Three ways to do it

Testing gRPC: by hand, with In-process stub server, or with Keploy

A stub server returns whatever your hand-written implementation says, and drifts the moment the upstream proto changes. Keploy replays the real response bytes, so re-recording surfaces the change.

Select any row for the full comparison, with code.

Same coverage, three costs

What you write for gRPC vs what Keploy records

All three produce the same assertion. Only the third still passes after the next refactor without anyone editing it.

By hand2–4 hours
fake_payments_service.go
hand-written
// Hand-written stub implementing another team's generated interface.
package testsupport
 
import (
"context"
pb "github.com/acme/payments/gen/paymentspb"
)
 
type FakePaymentsServer struct {
pb.UnimplementedPaymentsServer
}
 
func (FakePaymentsServer) Authorize(
ctx context.Context, req *pb.AuthorizeRequest,
) (*pb.AuthorizeResponse, error) {
// Always OK. Retry, fallback, and deadline paths never run.
return &pb.AuthorizeResponse{
Status: pb.Status_AUTHORIZED,
ChargeId: "ch_1",
// A field the payments team added last sprint is simply absent.
}, nil
}

This compiles against your checked-in proto, so it keeps passing after the payments team adds a required field your code now reads.

In-process stub server1–2 hours
orders_grpc_test.go
tool-assisted
// In-process stub server on a bufconn channel.
package orders
 
import (
"context"
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/test/bufconn"
)
 
func TestConfirmOrder(t *testing.T) {
lis := bufconn.Listen(1024 * 1024)
srv := grpc.NewServer()
pb.RegisterPaymentsServer(srv, FakePaymentsServer{})
go srv.Serve(lis)
defer srv.Stop()
 
conn, _ := grpc.DialContext(context.Background(), "bufnet",
grpc.WithContextDialer(dialer(lis)), grpc.WithInsecure())
defer conn.Close()
 
svc := NewOrderService(pb.NewPaymentsClient(conn))
 
order, err := svc.Confirm(context.Background(), "ord_1")
 
if err != nil || order.State != "CONFIRMED" {
t.Fatalf("got %v err %v", order, err)
}
}

Clean and fast — and the response still comes from FakePaymentsServer, so the test can only be as accurate as that hand-maintained stub.

With Keploy~5 minutes
mocks.yaml
auto-generated
# Recorded with: keploy record -c './orders-service'
# The real gRPC exchange, captured as HTTP/2 frames.
version: api.keploy.io/v1beta1
kind: gRPC
name: mock-6
spec:
request:
method: "/acme.payments.Payments/Authorize"
metadata:
content-type: "application/grpc"
authorization: "Bearer <captured>"
# The serialised Protobuf message, exactly as sent.
body_base64: "CgdvcmRfMRIHdG9rXzEyMxgY"
response:
status_code: 0
status_message: "OK"
body_base64: "CAESBWNoXzEaCkFVVEhPUklaRUQ="
trailers:
grpc-status: "0"
x-payments-region: "us-west-2"

The Protobuf bytes and the trailing metadata are what the real payments service returned, so a proto change shows up as a diff on the next re-record.

Times are estimates for authoring one endpoint’s coverage from scratch, not measurements.

Keploy vs the alternatives

gRPC testing tools, compared

The options a team on service and platform integrations actually reaches for, and where each one genuinely wins. Select a row for the full comparison.

Best in classStrongPartialNot covered

Assessments reflect each tool’s documented behaviour, not benchmark measurements.

How it works

Record your gRPC app once, replay it forever

Keploy sits below your gRPC process at the network layer. It watches the calls your app already makes, then serves them back on replay so tests run with no dependencies attached.

Keploy records a GET call to /api/v1/orders/{id} on a gRPC service and captures the dependency calls it makes.

gRPC logo
Your gRPC app
GET/api/v1/orders/{id}200
RecordingKeploy proxyeBPF · userspace
+4 more gRPC clients
An example shape of a captured call. Your own endpoints and dependencies come from your real traffic, so nothing here has to be written by hand.
Mock coverage

gRPC clients Keploy records, driver by driver

Keploy captures gRPC at the wire protocol, so 5 of these 8 clients need no adapter, no test double, and no gRPC instance in CI.

Quick start

Your first gRPC test suite in under five minutes

Every command below runs against your existing gRPC service. Nothing in your source tree changes.

  1. 1Install the Keploy CLI

    One binary on the recording machine. No interceptor is registered and no proto is regenerated.

    curl -sSL https://keploy.io/install.sh | bash
  2. 2Record with your dependencies reachable

    Run the service as usual against real downstream gRPC services. Requests, responses, statuses, and trailers are captured.

    keploy record -c "./orders-service"
  3. 3Exercise the RPCs that matter

    Drive the service through its own API, or call it directly with grpcurl. Both record the downstream calls it makes.

    grpcurl -plaintext -d '{"order_id":"ord_1"}' localhost:50051 acme.orders.Orders/Confirm
  4. 4Replay with dependencies switched off

    Take the downstream services offline and run the suite. If replay passes, every RPC your code makes is answered by a recorded mock.

    keploy test -c "./orders-service" --delay 10

Ready to try it on your own gRPC service?

Ecosystem

Works with the rest of your gRPC stack

Keploy records at the network layer, so framework and driver choices inside your gRPC app do not change how it captures traffic.

FAQ

gRPC testing with Keploy: common questions

Join our GlobalCommunity

Connect with developers worldwide. Follow updates, ask questions, share feedback, and ship faster with other Keploy builders.

1.2M+Installs
18.4K+GitHub
100K+Devs
300M+Mocks
1K+Contributors
#1OSS Trending
4.9★★★★★from 500+ reviews onG2GartnerVS CodeChrome
★★★★★

Best report of integration and API tests I've seen — which we don't get from RestAssured.

G2
★★★★★

Future of microservices testing. I don't write tests now!

G2 · 5/5
★★★★★

An amazing product that simplifies the automation.

Gartner · 4.0
XGitHubSlackYouTubeLinkedIn
Built by developers, for developers.Let's build the future, together.