Skip to content

gRPC Examples

Practical examples for testing gRPC services with Testurio.

Type-Safe Service Definition

gRPC service definitions follow the sync request / response shape, with RPC method names as the operation IDs. The same interface powers both the client (typed request(...) payloads) and the server (typed mockResponse((req) => ...) callbacks).

GrpcStreamProtocol uses the async clientMessages / serverMessages shape — see the streaming section below for an example.

If you're generating schemas from .proto, jump to Schema-First gRPC at the bottom of the page.

Unary RPC

typescript
import { Client, Server, TestScenario, testCase } from 'testurio';
import { GrpcUnaryProtocol } from '@testurio/protocol-grpc';

interface UserService {
  GetUser: {
    request: { user_id: number };
    response: { id: number; name: string; email: string };
  };
  CreateUser: {
    request: { name: string; email: string };
    response: { id: number; name: string; email: string };
  };
}

const PROTO_PATH = 'proto/user-service.proto';
const SERVICE_NAME = 'user.v1.UserService';

const grpcServer = new Server('grpc-backend', {
  protocol: new GrpcUnaryProtocol<UserService>({
    protoPath: PROTO_PATH,
    serviceName: SERVICE_NAME,
  }),
  listenAddress: { host: 'localhost', port: 50051 },
});

const grpcClient = new Client('grpc-client', {
  protocol: new GrpcUnaryProtocol<UserService>({
    protoPath: PROTO_PATH,
    serviceName: SERVICE_NAME,
  }),
  targetAddress: { host: 'localhost', port: 50051 },
});

const scenario = new TestScenario({
  name: 'User gRPC Service',
  components: [grpcServer, grpcClient],
});

Get User

typescript
const tc = testCase('Get user by ID', (test) => {
  const client = test.use(grpcClient);
  const server = test.use(grpcServer);

  server.onRequest('GetUser', { user_id: 1 }).mockResponse(() => ({
    id: 1,
    name: 'Alice',
    email: 'alice@example.com',
  }));

  client.request('GetUser', { user_id: 1 });

  client.onResponse('GetUser').assert((res) => {
    return res.id === 1 && res.name === 'Alice';
  });
});

Create User

typescript
const tc = testCase('Create new user', (test) => {
  const client = test.use(grpcClient);
  const server = test.use(grpcServer);

  server.onRequest('CreateUser').mockResponse((req) => ({
    id: 100,
    name: req.name ?? 'Unknown',
    email: req.email ?? 'unknown@example.com',
  }));

  client.request('CreateUser', { name: 'Bob', email: 'bob@example.com' });

  client.onResponse('CreateUser').assert((res) => {
    return res.name === 'Bob' && res.email === 'bob@example.com';
  });
});

Shared Mock Responses with init()

typescript
scenario.init((test) => {
  const server = test.use(grpcServer);

  server.onRequest('GetUser', { user_id: 1 }).mockResponse(() => ({
    id: 1,
    name: 'Alice',
    email: 'alice@example.com',
  }));

  server.onRequest('CreateUser').mockResponse((req) => ({
    id: 100,
    name: req.name ?? 'Unknown',
    email: req.email ?? 'unknown@example.com',
  }));
});

// Test cases use the shared mocks
const tc = testCase('Get user by ID', (test) => {
  const client = test.use(grpcClient);

  client.request('GetUser', { user_id: 1 });
  client.onResponse('GetUser').assert((res) => res.name === 'Alice');
});

Bidirectional Streaming

typescript
import { AsyncClient, AsyncServer, TestScenario, testCase } from 'testurio';
import { GrpcStreamProtocol } from '@testurio/protocol-grpc';

interface ChatStream {
  clientMessages: {
    ChatMessage: { userId: string; text: string };
  };
  serverMessages: {
    ChatMessage: { userId: string; text: string; timestamp: number };
    SystemMessage: { text: string };
  };
}

const streamServer = new AsyncServer('chat-server', {
  protocol: new GrpcStreamProtocol<ChatStream>({
    protoPath: 'proto/chat.proto',
    serviceName: 'chat.v1.ChatService',
    methodName: 'Chat',
  }),
  listenAddress: { host: 'localhost', port: 50052 },
});

const streamClient = new AsyncClient('chat-client', {
  protocol: new GrpcStreamProtocol<ChatStream>({
    protoPath: 'proto/chat.proto',
    serviceName: 'chat.v1.ChatService',
    methodName: 'Chat',
  }),
  targetAddress: { host: 'localhost', port: 50052 },
});

const tc = testCase('Chat stream', (test) => {
  const client = test.use(streamClient);
  const server = test.use(streamServer);

  server.onMessage('ChatMessage').mockEvent('ChatMessage', (payload) => ({
    userId: payload.userId,
    text: `Echo: ${payload.text}`,
    timestamp: Date.now(),
  }));

  client.sendMessage('ChatMessage', { userId: 'alice', text: 'Hello!' });

  client.waitEvent('ChatMessage').timeout(2000).assert((msg) => {
    return msg.text.includes('Echo:');
  });
});

Schema-First gRPC

bash
testurio generate user-service.proto -o ./generated/user-service.schema.ts
typescript
import { userServiceSchema } from './generated/user-service.schema';

const grpcServer = new Server('grpc-backend', {
  protocol: new GrpcUnaryProtocol({
    protoPath: PROTO_PATH,
    serviceName: SERVICE_NAME,
    schema: userServiceSchema,
  }),
  listenAddress: { host: 'localhost', port: 50051 },
});

Released under the MIT License.