Configuring the HTTP Client and Server for Test Stability
To prevent goroutine leaks when a httptest.Server handler hangs, you must replace http.DefaultClient with a custom http.Client that defines a explicit Timeout. Because the default client has no timeout, a stalled mock server will cause the test goroutine to block indefinitely, potentially hanging the entire CI/CD pipeline.
Client Configuration for Leak Prevention
Initialize a scoped client within your test function or as part of your application's dependency injection. This ensures that the request is terminated regardless of the server's state.
// Recommended client configuration for tests
client := &http.Client{
Timeout: 5 * time.Second,
}
Server Decommissioning and Port Management
To avoid port exhaustion and ensure a clean state across multiple test cases, use the defer server.Close() pattern immediately after the server is instantiated. This ensures the listener is closed and the associated resources are released as soon as the test function returns.
For suites with many sub-tests, the recommended approach is to instantiate the server within the specific t.Run block rather than globally. This isolates the mock behavior and ensures a fresh port is allocated and released for every single case.
Implementation Pattern
The following structure demonstrates the safe integration of a timed-out client and a decommissioned server:
func TestAPIInteraction(t *testing.T) {
// 1. Setup mock server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "success"}`))
}))
// 2. Ensure decommissioning to prevent port exhaustion
defer server.Close()
// 3. Configure client to prevent goroutine leaks
client := &http.Client{
Timeout: 2 * time.Second,
}
// Inject server.URL into your application logic
resp, err := client.Get(server.URL)
if err != nil {
t.Fatalf("Request failed: %v", err)
}
defer resp.Body.Close()
}
Assumptions and Verification
This approach assumes the use of Go 1.12 or later. To verify that the timeout is functioning correctly, you can temporarily introduce a time.Sleep longer than the client timeout inside the httptest.Server handler; the test should fail with a context deadline exceeded error rather than hanging.
Diagnostic Detail: Are you using a shared http.Transport across multiple clients? If so, the transport's IdleConnTimeout may also need configuration to ensure connections are fully purged.