oapitesthandler
A code generator that creates test handlers for testing HTTP clients generated
by oapi-codegen. It generates mock HTTP servers that implement the
oapi-codegen strict server interface, allowing you to set expectations on API calls in your tests.
The generated code has type-safe methods for setting expected requests and their corresponding responses. It validates
that all expectations are met during test execution and that no unexpected requests are made.
petstore_test.go has several examples of how to use the generated handler.
Set go:generate directives near where you are already generating your oapi-codegen client/server. For example:
//go:generate go tool oapi-codegen -config ./oapi-codegen.yaml ./openapi.yaml
//go:generate go tool oapitesthandler --config=./oapi-codegen.yaml --out=internal/testhandler ./openapi.yaml
Installation
With go install
go install github.com/willabides/oapitesthandler/cmd/oapitesthandler@latest
go get -tool github.com/willabides/oapitesthandler/cmd/oapitesthandler
bindown template-source add oapitesthandler https://github.com/WillAbides/oapitesthandler/releases/latest/download/bindown.yaml
bindown dependency add oapitesthandler --source oapitesthandler -y
Usage
The generated TestHandler provides a type-safe way to mock API endpoints in your tests.
Basic Pattern
func TestMyService(t *testing.T) {
// Create the test handler
handler := petstoretest.NewTestHandler(t)
server := httptest.NewServer(handler)
defer server.Close()
// Create your service that uses an oapi-codegen client
client, err := oapi.NewClientWithResponses(server.URL)
require.NoError(t, err)
petstoreService := NewPetstoreService(client)
// Set expectations
handler.ExpectGetPetById(1).RespondJSON200(petstoretest.GetPetById200JSONResponse{
Id: 1,
Name: "Fluffy",
})
// Call your service - it hits the mock server
pet, err := petstoreService.GetPetByID(t.Context(), 1)
require.NoError(t, err)
require.Equal(t, "Fluffy", pet.Name)
// Test automatically verifies all expectations were met
}
Setting Expectations
Each API operation gets an Expect{OperationName} method that returns a builder. Chain a Respond method to set the
response:
// Different response codes
handler.ExpectGetPetById(1).RespondJSON200(successResponse)
handler.ExpectGetPetById(999).RespondJSON404(notFoundResponse)
// Operations with request bodies
handler.ExpectUpdatePet(updateRequest).RespondJSON200(updatedPet)
// Operations with query parameters
handler.ExpectFindPetsByStatus(queryParams).RespondJSON200(pets)
Controlling Call Counts
Use Times() to expect multiple calls with the same parameters:
// Expect exactly 3 calls
handler.ExpectGetPetById(1, petstoretest.Times(3)).RespondJSON200(response)
Use MinTimes() when you want at least N calls:
// Expect at least 2 calls, but allow more
handler.ExpectGetPetById(1, petstoretest.MinTimes(2)).RespondJSON200(response)
// MinTimes(0) acts as a stub - accepts any number of calls including zero
handler.ExpectGetPetById(1, petstoretest.MinTimes(0)).RespondJSON200(response)
Custom Responses
Use Handle() for responses that aren't described in your OpenAPI spec:
handler.ExpectGetPetById(1).Handle(func(req petstoretest.GetPetByIdRequestObject, w http.ResponseWriter) {
w.Header().Set("X-Custom-Header", "value")
w.WriteHeader(200)
err := json.NewEncoder(w).Encode(map[string]any{
"id": req.PetId,
"name": fmt.Sprintf("Pet-%d", req.PetId),
})
if err != nil {
t.Errorf("failed to write response: %v", err)
}
})
CLI Usage
Usage: oapitesthandler --config=STRING --out=STRING <spec> [flags]
Arguments:
<spec> Path to OpenAPI spec YAML file
Flags:
-h, --help Show context-sensitive help.
-c, --config=STRING Path to oapi-codegen config YAML file
-o, --out=STRING Directory to write the generated test handler to
--models=STRING Path to a package containing the OpenAPI models. If not specified, models
will be generated into the output directory.
--version Output the oapitesthandler version and exit.