Documentation
¶
Overview ¶
Package httpmock provides a simple, declarative API for testing HTTP clients.
The package centers around the Server type, which wraps httptest.Server to provide a way to specify expected HTTP requests and their corresponding responses. This makes it easy to verify that your HTTP client makes the expected calls in the expected order.
Basic usage:
server := httpmock.NewServer(t, []httpmock.Exchange{{ Request: httpmock.Request{ Method: "GET", Path: "/api/users", Headers: map[string]string{ "Authorization": "Bearer token123", }, }, Response: httpmock.Response{ StatusCode: http.StatusOK, Body: map[string]interface{}{ "users": []string{"alice", "bob"}, }, }, }}) defer server.Close() // Use server.Path("/api/users") as the base URL for your HTTP client...
The Server will verify that requests match the expected method, path, headers, and body (if specified). For bodies, it supports both exact string matches and JSON comparison. You can also provide custom validation functions for more complex request validation:
Request: httpmock.Request{ Method: "POST", Path: "/api/users", Validate: func(r *http.Request) error { if r.Header.Get("Content-Length") == "0" { return fmt.Errorf("empty request body") } return nil }, }
After your test completes, calling Close will shut down the server and verify that all expected requests were received:
server := httpmock.NewServer(t, []httpmock.Exchange{...}) defer server.Close() // will fail the test if not all expectations were met
You can also verify expectations explicitly at any point using VerifyComplete:
if err := server.VerifyComplete(); err != nil { t.Error("not all expected requests were made") }
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Request ¶
type Request struct { Method string Path string Body any // optional: if non-empty, we check the body as JSON (or raw string) Headers map[string]string // optional: headers that must be present // Validate is an optional callback for additional checks. Validate func(r *http.Request) error }
Request specifies what an incoming HTTP request should look like.
func MergeRequests ¶
MergeRequests creates a new Request by combining multiple requests. Later requests override values from earlier requests. Non-zero/non-empty values from each request override values from previous requests.
type Response ¶
type Response struct { StatusCode int // HTTP status code (defaults to 200 OK if not set) Body any // can be a JSON-marshalable object or a string Headers map[string]string // optional: headers to include in response }
Response specifies how to respond to a matched request.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server provides a declarative API on top of httptest.Server for testing HTTP clients. It allows you to specify a sequence of expected requests and their corresponding responses, making it easy to verify that your HTTP client makes the expected calls in the expected order.
Example (Basic) ¶
1. Examples
testServer := NewServer(&mockT{}, []Exchange{{ Request: Request{ Method: "GET", Path: "/hello", }, Response: Response{ Body: "world", }, }}) defer testServer.Close() resp, _ := http.Get(testServer.Path("/hello")) body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))
Output: world
Example (JsonRequest) ¶
testServer := NewServer(&mockT{}, []Exchange{{ Request: Request{ Method: "POST", Path: "/api/users", Body: `{"name":"Alice"}`, Headers: map[string]string{ "Content-Type": "application/json", }, }, Response: Response{ StatusCode: http.StatusCreated, Body: map[string]interface{}{ "id": 1, "name": "Alice", }, }, }}) defer testServer.Close() resp, _ := http.Post(testServer.Path("/api/users"), "application/json", strings.NewReader(`{"name":"Alice"}`)) body, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode) fmt.Println(string(body))
Output: 201 {"id":1,"name":"Alice"}
Example (Sequence) ¶
testServer := NewServer(&mockT{}, []Exchange{ { Request: Request{ Method: "POST", Path: "/login", Body: `{"username":"alice","password":"secret"}`, }, Response: Response{ Body: map[string]string{"token": "abc123"}, }, }, { Request: Request{ Method: "GET", Path: "/profile", Headers: map[string]string{ "Authorization": "Bearer abc123", }, }, Response: Response{ Body: map[string]string{"name": "Alice"}, }, }, }) defer testServer.Close() // Login resp, _ := http.Post(testServer.Path("/login"), "application/json", strings.NewReader(`{"username":"alice","password":"secret"}`)) var loginResp struct{ Token string } err := json.NewDecoder(resp.Body).Decode(&loginResp) if err != nil { fmt.Println("decode error:", err) return } resp.Body.Close() // Get profile using token req, _ := http.NewRequest(http.MethodGet, testServer.Path("/profile"), nil) req.Header.Set("Authorization", "Bearer "+loginResp.Token) resp, _ = http.DefaultClient.Do(req) body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))
Output: {"name":"Alice"}
Example (Validation) ¶
testServer := NewServer(&mockT{}, []Exchange{{ Request: Request{ Method: "POST", Path: "/upload", Validate: func(r *http.Request) error { if r.Header.Get("Content-Length") == "0" { return fmt.Errorf("empty request body") } return nil }, }, Response: Response{ StatusCode: http.StatusOK, Body: "uploaded", }, }}) defer testServer.Close() // Send non-empty request resp, _ := http.Post(testServer.Path("/upload"), "text/plain", strings.NewReader("some data")) body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))
Output: uploaded
func NewServer ¶
NewServer creates and starts an httptest.Server that will match incoming requests to the provided expectations in order.
func (*Server) Close ¶
func (s *Server) Close()
Close shuts down the underlying httptest.Server and verifies all expected exchanges were completed.
func (*Server) Path ¶
Path returns the complete URL for the given path. For example, if the server URL is "http://localhost:12345" and path is "/api/users", this returns "http://localhost:12345/api/users". url.JoinPath handles all path normalization, including handling of leading/trailing slashes.
func (*Server) VerifyComplete ¶
VerifyComplete checks that all expected exchanges were completed.