16 — Testing, code generation, and compatibility

Goal

Make your Go service and generated API reproducible and safe to change.

Step 1: Test domain behavior

Keep service rules in ordinary Go types/functions. Write table-driven tests for title validation, legal status transitions, and not-found behavior. Each test should name its case, run the operation, and compare the result. Run go test ./... from the repo root.

Step 2: Test gRPC handlers

Construct the handler with a fake repository, call its method directly, and assert both the response and the status code. For status codes, use status.Code(err). Cover invalid input, missing rows, database errors, and authorization failures.

Step 3: Test through a generated client

Use google.golang.org/grpc/test/bufconn to run a gRPC server in memory. Register the service on a test server, dial it using a custom context dialer, create the generated TaskServiceClient, and invoke GetTask. This catches mistakes in registration and wire handling without opening a TCP port. Always stop the server and close the connection with defer.

bufconn provides an in-memory connection that exercises the gRPC client/server stack without opening a local port. That makes an integration test fast and independent of port conflicts. Keep domain unit tests too: they fail closer to the source of a logic problem and are easier to understand.

Step 4: Verify generated code

Keep the .proto source in the repo. Add a PowerShell script scripts/generate.ps1 containing the two protoc commands from chapter 8. From a clean checkout run the script and inspect git diff --exit-code -- gen/go flutter_app/lib/src/generated; a non-empty diff means checked-in generated output is stale. If you choose not to check generated files in, run generation during the build instead and document the required tools.

Step 5: Check compatibility before editing

For each proto change, ask: Does an old client still parse the new message? Did any field's type or meaning change? Were removed tags and names reserved? Is a new enum value handled safely by old clients? Can the server handle omitted fields? Never renumber a tag. Add a compatibility review to pull requests.

Compatibility matters because mobile clients do not all update at the same time. A server may be updated while an older app is still installed. Additive changes are generally safer than repurposing or removing fields, but every new enum value and default behavior should still be considered by older clients.

Step 6: Run quality commands

Run gofmt on the Go files under server, then run go vet ./..., go test ./..., and go test -race ./... from the repository root. Run flutter analyze from the flutter_app directory.

Checkpoint

Delete generated directories, regenerate them, then confirm the Go server and Flutter app still build. Verify tests fail when you deliberately break a validation rule, then restore the code.

Practice

Deprecate a field in a future proto revision while keeping old clients compatible. Reserve the removed field's name and number after it is deleted.

results matching ""

    No results matching ""