What is gRPC?
gRPC is an open-source RPC framework built on HTTP/2. Instead of calling URLs with JSON bodies, clients call methods defined in a .proto contract. Messages are serialized with Protocol Buffers — a binary format that's smaller and faster to parse than JSON.
REST API
- URL endpoints & HTTP verbs
- JSON text format
- Swagger / OpenAPI docs
- HTTP/1.1 by default
gRPC
- Named methods in .proto
- Binary Protocol Buffers
- .proto file is the contract
- HTTP/2 multiplexing
Four Call Patterns
gRPC supports four call types. QA must know which pattern a method uses — it determines how you structure your test.
Unary
One request, one response. The most common pattern.
client ◀── server
Server Streaming
One request, many responses streamed back.
client ◀═══ server
Client Streaming
Many requests sent, one final response.
client ◀── server
Bidirectional
Both sides stream simultaneously.
client ◀═══ server
Live Endpoints
Testing with Postman
- Open Postman → click New → select gRPC Request
- Enter server URL: grpc.malikova.org:50051
- Click Import a .proto file → select testlab.proto
- Pick method from dropdown: UserService / GetUser
- Paste the request body below and click Invoke
- Validate the response against the expected result
Test Data
These emails are hardcoded in the blacklist. Use them to verify the CheckBlacklist method returns the correct status.
- bad@email.com
- spam@test.com
Request
{ "email": "bad@email.com" }
Expected Response
{ "blocked": true }
Test Verdict
| Condition | Expected field | Verdict |
|---|---|---|
| Email is in blacklist | blocked: true | PASS |
| Email is NOT in blacklist | blocked: false | PASS |
| Blacklisted email returns false | — | FAIL |
| Empty email field sent | gRPC status INVALID_ARGUMENT | CHECK |
| Service unreachable | gRPC status UNAVAILABLE | FAIL |
testlab.proto
The .proto file is the single source of truth. It replaces Swagger. Before writing any test you must read this file and understand every message field and its type.
syntax = "proto3"; package testlab; // ── Enum ───────────────────────────────────── enum UserStatus { USER_STATUS_UNKNOWN = 0; USER_STATUS_ACTIVE = 1; USER_STATUS_BLOCKED = 2; } // ── Services ───────────────────────────────── service UserService { rpc GetUser (UserRequest) returns (UserResponse); } service BlacklistService { rpc CheckBlacklist (BlacklistRequest) returns (BlacklistResponse); } // ── Messages ───────────────────────────────── message UserRequest { string email = 1; } message UserResponse { string email = 1; UserStatus status = 2; // enum — not a raw string } message BlacklistRequest { string email = 1; } message BlacklistResponse { bool blocked = 1; }
How QA Uses a .proto File
- Find the service — the service block names what you're connecting to. This maps to the server address.
- Find the method — each rpc line is a callable method. This is what you select in Postman.
- Read the request message — every field listed is what you send. The number (= 1) is the field tag — used internally by protobuf, not by you.
- Read the response message — these are the fields you assert on in your test. Check name, type, and expected value.
- Check field types — string, bool, int32, repeated, etc. A type mismatch is a valid bug to report.
Common Proto3 Types
| Proto type | Maps to | Example |
|---|---|---|
| string | UTF-8 text |
"user@example.com"
|
| bool | true / false |
true
|
| int32 | Integer number |
42
|
| repeated | Array / list |
["a", "b"]
|
| enum | Named constants |
USER_STATUS_ACTIVE
|
What to Assert On
Every gRPC response carries a status code — not HTTP 200/404, but gRPC's own code system. A passing test must validate both the response body and the status code.
| Code | Name | When to expect it |
|---|---|---|
| 0 | OK | Request succeeded. Always assert this on happy-path tests. |
| 1 | CANCELLED | Client cancelled the request before completion. |
| 2 | UNKNOWN | Server threw an unhandled exception. Typically a bug. |
| 3 | INVALID_ARGUMENT | Sent a field with a bad value (wrong format, empty required field). Test this on negative cases. |
| 4 | DEADLINE_EXCEEDED | Response did not arrive within the client timeout. Useful for performance tests. |
| 5 | NOT_FOUND | Requested entity doesn't exist. Test with an unknown email/ID. |
| 6 | ALREADY_EXISTS | Attempt to create something that already exists. Common in create/register flows. |
| 7 | PERMISSION_DENIED | Caller is authenticated but not authorized for this action. |
| 13 | INTERNAL | Serious internal server error. Always a defect to report. |
| 14 | UNAVAILABLE | Service is down or unreachable. Check infra before reporting code bug. |
| 16 | UNAUTHENTICATED | Missing or invalid token / credentials. Test with and without auth headers. |
What to Test Beyond Happy Path
- Empty required field — send { "email": "" }. Expect INVALID_ARGUMENT.
- Malformed email — send { "email": "notanemail" }. Expect INVALID_ARGUMENT or a business-level error.
- Unknown user — send a valid-format email that doesn't exist in the system. Expect NOT_FOUND or blocked: false.
- Missing metadata / auth header — omit any required token. Expect UNAUTHENTICATED.
- Duplicate registration — if a create method exists, call it twice with the same data. Expect ALREADY_EXISTS.