· обновлено
Как тестировать gRPC-сервисы
Тестирование gRPC начинается не с JSON и URL, а со схемы. Клиенту нужно знать сервис, метод и protobuf-типы, иначе он не сможет закодировать сообщение. Когда схема загружена, работа напоминает обычную проверку API: вы задаёте адрес, metadata и тело, отправляете вызов и разбираете статус. Разница проявляется в потоках, deadline и TLS.
Ниже мы используем grpcurl для воспроизводимых команд. Те же шаги доступны в графических клиентах с поддержкой gRPC, включая Tetiva: подключить reflection или .proto, выбрать метод, заполнить сообщение и отправить вызов.
Тестовая схема
Представим сервис заказов с обычным методом и серверным потоком:
syntax = "proto3";
package shop.v1;
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc WatchOrders(WatchOrdersRequest) returns (stream Order);
rpc ImportOrders(stream CreateOrderRequest) returns (ImportSummary);
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
message GetOrderRequest {
string id = 1;
}
message WatchOrdersRequest {
string customer_id = 1;
}
message CreateOrderRequest {
string customer_id = 1;
repeated string sku = 2;
}
message Order {
string id = 1;
string status = 2;
}
message ImportSummary {
int32 accepted = 1;
}
message ChatMessage {
string text = 1;
}
Полное имя unary-метода — shop.v1.OrderService/GetOrder. Пакет важен: вызов только OrderService/GetOrder часто заканчивается UNIMPLEMENTED, хотя сервер работает.


Server reflection: получить схему от сервера
Server reflection позволяет клиенту запросить список сервисов и их дескрипторы во время выполнения. Для разработки это самый быстрый путь: не нужно искать совпадающую версию .proto.
Проверьте, включён ли reflection:
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 list shop.v1.OrderService
grpcurl -plaintext localhost:50051 describe shop.v1.OrderService.GetOrder
Если сервер отвечает, выберите метод и отправьте сообщение:
grpcurl -plaintext \
-d '{"id":"ord_123"}' \
localhost:50051 \
shop.v1.OrderService/GetOrder
В production reflection часто отключают, чтобы не публиковать каталог сервисов. Тогда передайте исходные .proto и пути импортов:
grpcurl -plaintext \
-import-path ./proto \
-proto shop/v1/orders.proto \
-d '{"id":"ord_123"}' \
localhost:50051 \
shop.v1.OrderService/GetOrder
Если схема импортирует google/type/date.proto, одного главного файла недостаточно: добавьте каталог, внутри которого начинается путь google/type/date.proto.
Как получить пример запроса из схемы
Не придумывайте JSON по названию метода. Откройте входной message в дескрипторе и заполните поля с учётом protobuf JSON mapping. Имена можно писать как customerId; многие инструменты принимают и исходное customer_id. Значения int64 в JSON обычно передаются строкой, enum — именем, bytes — строкой Base64, Timestamp — временем в RFC 3339.
Для CreateOrderRequest минимальный осмысленный пример выглядит так:
{
"customerId": "cus_42",
"sku": ["book-1", "pen-2"]
}
Автогенерация в API-клиенте экономит время, но результат остаётся заготовкой. Пустая строка, ноль и первый enum-элемент — допустимые значения protobuf, однако бизнес-валидацию они могут не пройти. В Tetiva пример тела строится из загруженной схемы; перед отправкой замените заглушки доменными значениями.
Unary и три вида streaming
У gRPC четыре формы вызова:
- Unary: один запрос, один ответ. Его проще всего повторять и включать в smoke-тесты.
- Server streaming: один запрос, несколько ответов. Проверяйте порядок событий, завершение потока и поведение при отмене клиентом.
- Client streaming: несколько запросов, один итоговый ответ. Важно явно закрыть отправляющую сторону, иначе сервер продолжит ждать сообщения.
- Bidirectional streaming: обе стороны отправляют сообщения независимо. Тестируйте не только содержимое, но и последовательность: ответ не обязан соответствовать последнему отправленному сообщению один к одному.
Серверный поток в grpcurl запускается обычной командой; ответы печатаются по мере поступления:
grpcurl -plaintext \
-d '{"customerId":"cus_42"}' \
localhost:50051 \
shop.v1.OrderService/WatchOrders
Для клиентского и двунаправленного потока удобнее интерактивный режим или графический клиент: откройте соединение, отправьте несколько сообщений, затем завершите клиентскую половину потока. Зафиксируйте ожидаемые события и таймаут ожидания, иначе зависший тест будет выглядеть как медленный.
Metadata и аутентификация
Metadata — это пары ключ-значение поверх HTTP/2. Ключи пишутся в нижнем регистре; бинарные ключи оканчиваются на -bin. Токен обычно передают так:
grpcurl -plaintext \
-H 'authorization: Bearer dev-token' \
-H 'x-request-id: test-001' \
-d '{"id":"ord_123"}' \
localhost:50051 \
shop.v1.OrderService/GetOrder
Если приходит UNAUTHENTICATED, сначала проверьте наличие и формат токена. PERMISSION_DENIED чаще означает, что личность уже установлена, но у неё нет права на действие. Не сохраняйте рабочие токены прямо в коллекции: используйте локальное окружение или хранилище секретов.
Deadline: ограничить ожидание
У каждого тестового вызова должен быть deadline. Без него потерянный пакет, зависшая зависимость или незакрытый stream могут удерживать проверку неопределённо долго.
grpcurl -plaintext \
-max-time 3 \
-d '{"id":"ord_123"}' \
localhost:50051 \
shop.v1.OrderService/GetOrder
Статус DEADLINE_EXCEEDED не доказывает, что сервер ничего не сделал: операция могла завершиться после того, как клиент перестал ждать. Для изменяющих запросов используйте идемпотентный ключ и проверяйте серверные логи по request ID. Слишком короткий deadline создаёт нестабильные тесты, слишком длинный скрывает деградацию; выбирайте предел из бюджета конкретной операции.
TLS, mTLS и частые ошибки адреса
Для локального сервера без TLS нужен -plaintext. Для защищённого адреса флаг убирают:
grpcurl api.example.com:443 list
С собственным центром сертификации передайте CA, а для mTLS — клиентский сертификат и ключ:
grpcurl \
-cacert ./certs/ca.pem \
-cert ./certs/client.pem \
-key ./certs/client-key.pem \
api.example.com:443 list
Ошибка certificate is valid for ... указывает на несовпадение имени хоста в сертификате. Подключение по IP вместо DNS-имени также меняет TLS SNI и маршрутизацию прокси. Отключать проверку сертификата допустимо лишь для локальной диагностики: такой обход не должен попадать в сохранённый production-профиль.
Минимальный чек-лист
Сначала подтвердите адрес и режим TLS, затем загрузите схему через reflection или .proto. Проверьте полное имя метода, сгенерируйте тело по message-типу, добавьте metadata и разумный deadline. Для streaming отдельно протестируйте отправку, получение, закрытие и отмену. Наконец, сохраните удачный вызов как коллекционный тест — так ручная находка станет повторяемой проверкой.