Lesson 24 / 25

Testing gRPC Services

Unit, in-process and contract tests.

Test at several levels

Unit-test servicer methods directly by calling them with request messages and a fake context. For realistic tests without networking, run the server in-process (grpc-go's bufconn, in-process channels in Java and .NET, or a server on a random local port in Python) and call it through a real client stub, which exercises interceptors and serialisation. Contract safety comes from buf breaking and generated clients; add end-to-end tests for critical flows, and test deadlines, cancellation and error codes, not only success paths.

An in-process test in Python

pytest sketch starting a server on a free local port.

import grpc, pytest
from concurrent import futures

@pytest.fixture
def stub():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
    order_pb2_grpc.add_OrderServiceServicer_to_server(OrderService(repo=FakeRepo()), server)
    port = server.add_insecure_port("localhost:0")      # pick a free port
    server.start()
    with grpc.insecure_channel(f"localhost:{port}") as channel:
        yield order_pb2_grpc.OrderServiceStub(channel)
    server.stop(None)

def test_missing_order_returns_not_found(stub):
    with pytest.raises(grpc.RpcError) as err:
        stub.GetOrder(order_pb2.GetOrderRequest(id="nope"), timeout=1)
    assert err.value.code() == grpc.StatusCode.NOT_FOUND

Assert on status codes

Tests that check the exact status code keep error behaviour stable for clients.

Quick check: Why run the server in-process for tests?

  • It removes the need for proto files
  • It makes protobuf optional
  • It exercises real stubs, serialisation and interceptors without external networking
  • It disables deadlines
Answer

It exercises real stubs, serialisation and interceptors without external networking — Realistic yet fast.