Health Checks v3.9.3
WARNING
⚠️ EXPERIMENTAL FEATURE: The GripMock Embedded SDK is currently experimental. The API is subject to change without notice, and functionality may be modified in future versions. Use at your own risk.
Version history: Health service stubbing available since v3.9.3 (JSON stubs). Embedded SDK support since v3.9.3 (legacy API). Current v2 API since v3.16.0.
GripMock supports stubbing the standard gRPC health service:
grpc.health.v1.Health/Check— unary health statusgrpc.health.v1.Health/Watch— streaming health status (useful for testing health transitions)
This allows you to test client behavior for dependency health transitions (for example NOT_SERVING -> SERVING).
Protected service: gripmock
The service key gripmock is reserved for GripMock internal readiness.
- An internal stub is created automatically at server startup with
NOT_SERVINGstatus - When the server becomes ready, the status updates to
SERVING - User stubs targeting
service: "gripmock"are stored but always overridden by the internal stub
srv := sdk.NewServer(t, sdk.WithProtoFiles("examples/projects/greeter/service.proto"))
defer srv.Close()
// This stub is stored but always overridden by internal stub:
srv.ExpectUnary("/grpc.health.v1.Health/Check").
Match("service", "gripmock").
Return("status", "NOT_SERVING")
client := grpc_health_v1.NewHealthClient(srv.Conn())
resp, err := client.Check(t.Context(), &grpc_health_v1.HealthCheckRequest{
Service: "gripmock",
})
// Returns SERVING (internal stub)
require.NoError(t, err)
require.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING, resp.GetStatus())Mocking Check
func TestHealthCheckMockedViaSDK(t *testing.T) {
srv := sdk.NewServer(t, sdk.WithProtoFiles("examples/projects/greeter/service.proto"))
defer srv.Close()
srv.ExpectUnary("/grpc.health.v1.Health/Check").
Match("service", "examples.health.backend").
Return("status", "NOT_SERVING")
client := grpc_health_v1.NewHealthClient(srv.Conn())
resp, err := client.Check(t.Context(), &grpc_health_v1.HealthCheckRequest{
Service: "examples.health.backend",
})
require.NoError(t, err)
require.Equal(t, grpc_health_v1.HealthCheckResponse_NOT_SERVING, resp.GetStatus())
}gripmock service behavior
Requests for the gripmock service return the internal stub status:
func TestHealthCheckGripmockProtectedViaSDK(t *testing.T) {
srv := sdk.NewServer(t, sdk.WithProtoFiles("examples/projects/greeter/service.proto"))
defer srv.Close()
// Even with a stub that targets "gripmock"...
srv.ExpectUnary("/grpc.health.v1.Health/Check").
Match("service", "gripmock").
Return("status", "NOT_SERVING")
client := grpc_health_v1.NewHealthClient(srv.Conn())
resp, err := client.Check(t.Context(), &grpc_health_v1.HealthCheckRequest{
Service: "gripmock",
})
require.NoError(t, err)
// Internal stub overrides user stub — SERVING
require.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING, resp.GetStatus())
}Unknown service fallback
If no stub matches and the service is not gripmock, the request returns NotFound:
func TestHealthCheckUnknownServiceFallbackViaSDK(t *testing.T) {
srv := sdk.NewServer(t, sdk.WithProtoFiles("examples/projects/greeter/service.proto"))
defer srv.Close()
client := grpc_health_v1.NewHealthClient(srv.Conn())
resp, err := client.Check(t.Context(), &grpc_health_v1.HealthCheckRequest{
Service: "examples.health.unknown",
})
require.Error(t, err)
require.Equal(t, codes.NotFound, status.Code(err))
}Mocking Watch stream
The Watch method returns a stream of health status updates. You can stub it to return a sequence of statuses:
func TestRunHealthWatchMockedStreamViaSDK(t *testing.T) {
srv := sdk.NewServer(t, sdk.WithProtoFiles("examples/projects/greeter/service.proto"))
defer srv.Close()
srv.ExpectServerStream("/grpc.health.v1.Health/Watch").
Match("service", "examples.health.watch").
SendStream(
map[string]any{"status": "NOT_SERVING"},
map[string]any{"status": "SERVING"},
)
client := grpc_health_v1.NewHealthClient(srv.Conn())
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
stream, err := client.Watch(ctx, &grpc_health_v1.HealthCheckRequest{
Service: "examples.health.watch",
})
require.NoError(t, err)
first, err := stream.Recv()
require.NoError(t, err)
require.Equal(t, grpc_health_v1.HealthCheckResponse_NOT_SERVING, first.GetStatus())
second, err := stream.Recv()
require.NoError(t, err)
require.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING, second.GetStatus())
}Watch with delay
You can add a delay before individual stream messages using Delay:
srv.ExpectServerStream("/grpc.health.v1.Health/Watch").
Match("service", "examples.health.watch").
SendStream(
Delay(10*time.Millisecond, "status", "NOT_SERVING"),
map[string]any{"status": "SERVING"},
)Full runnable example
See:
examples/projects/health/stubs.yamlexamples/projects/health/case_check_mocked_not_serving.gctfexamples/projects/health/case_watch_mocked_stream.gctfexamples/projects/health/case_check_gripmock_protected.gctf