README
¶
extapigrpcsync
This example shows how to test the following scenario:
- Call the service internal API (inbound gRPC).
- During processing, the service performs an external outbound gRPC call.
- The external call is mocked and validated using JSONC data.
Why this example exists
It demonstrates a working pattern for a real project:
prepareconfigures external API expectations as acalls[]list (expected request+stub responsefor each call).preparereturns a normalized plan that can be reused in later steps.actioncalls your API usingservice_chainfrompreparedata via{{steps.<id>.response...}}.verifychecks external call side effects (whether the call happened, payload content, call count).assertvalidates theactionstep response.
Why there are three cases
All three cases validate our service API (target = call-api), but with different external call chain length and content:
external_grpc_sync_ok.jsonc: 1 external call, final API status =SERVING.external_grpc_sync_plan_response_ok.jsonc: 2 sequential external calls, final API status =NOT_SERVING.external_grpc_sync_verify_response_ok.jsonc: 3 sequential external calls, final API status =SERVICE_UNKNOWN.
In these cases, verify-external does not replace API verification — it only additionally confirms outbound side effects.
How it works
The harness starts two bufconn servers:
apiHealthServer— internal API of the service under test.externalHealthServer— external API mock.
apiHealthServer receives an injected externalClient and performs outbound calls exactly like production code.
externalHealthServer:
- Reads the call plan from harness state (filled by the
preparestep). - Stores the actual inbound payload in the call journal.
- Returns a predefined stub response.
- Fails if payload does not match the plan.
Minimal JSONC pattern
{
"name": "grpc api handler performs sync outbound grpc call",
"steps": [
{
"id": "plan-external",
"kind": "prepare",
"handler": "PlanExternalCheck",
"request": {
"calls": [
{
"expected_service": "order-100",
"stub_response": { "status": "SERVING" }
}
]
}
},
{
"id": "call-api",
"kind": "action",
"handler": "CallAPI",
"request": {
"service_chain": [
"{{steps.plan-external.response.calls.0.expected_service}}"
]
}
},
{
"id": "verify-external",
"kind": "verify",
"handler": "VerifyExternalCheck",
"request": {
"expected_services": [
"{{steps.plan-external.response.calls.0.expected_service}}"
],
"expected_calls": 1
}
}
],
"assert": {
"code": "OK",
"response_from_step": "call-api",
"response": { "status": "SERVING" }
}
}
PlanExternalCheck returns the following in this example:
{
"planned": true,
"calls": [
{
"expected_service": "...",
"stub_response": { "status": "SERVING" }
}
]
}
Therefore, subsequent steps can reference calls.N.expected_service without duplicating constants in JSONC.
How to adapt this to a real service
- In
NewCaseHarness, create your API server with real dependencies. - Replace the external gRPC dependency with a mock server (
bufconnor testcontainer). - Keep mock state in the harness:
- expectation plan (array of calls: expected request + stub response),
- journal of actual calls.
- Implement handlers:
PlanExternal...(prepare) — writes the plan,Call...(action) — calls your API,VerifyExternal...(verify) — validates call journal.
- In
assert.response_from_step, explicitly specify which step response should be compared.
Run
go test ./examples/extapigrpcsync
Documentation
¶
There is no documentation for this package.
Click to show internal directories.
Click to hide internal directories.