在Go语言项目里,HTTP客户端通常会请求第三方服务。为了让单元测试稳定可重复,我们应当把外部依赖替换成可控的模拟服务。标准库net/http/httptest正好提供了这样的能力,可以启动一个临时HTTP服务并自定义响应内容。

使用httptest模拟API响应
httptest.NewServer接受一个http.Handler,我们可以在闭包中判断请求路径并返回固定JSON。然后把它的URL赋值给客户端的BaseURL即可。
package main
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
)
type Client struct {
baseURL string
http *http.Client
}
func (c *Client) GetUser(id string) (map[string]interface{}, error) {
resp, err := c.http.Get(c.baseURL + "/users/" + id)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 读取响应体
body, _ := io.ReadAll(resp.Body)
var data map[string]interface{}
json.Unmarshal(body, &data)
return data, nil
}
func TestGetUser(t *testing.T) {
// 模拟外部API
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/users/1" {
w.WriteHeader(http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"id":1,"name":"tom"}`))
}))
defer srv.Close()
cli := &Client{baseURL: srv.URL, http: srv.Client()}
u, err := cli.GetUser("1")
if err != nil {
t.Fatal(err)
}
if u["name"] != "tom" {
t.Fatalf("期望tom,实际%v", u["name"])
}
}
验证请求参数与错误场景
除了正常返回,还可以模拟超时、状态码错误等情况。通过检查请求头或查询参数,能确认客户端是否按预期发送数据。
模拟500错误
func TestServerError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
cli := &Client{baseURL: srv.URL, http: srv.Client()}
_, err := cli.GetUser("2")
if err == nil {
t.Fatal("应当返回错误")
}
}
使用表驱动测试覆盖多种响应
Go推荐表驱动测试,把不同URL、响应体和期望结果写成用例,循环执行,既清晰又易维护。
| 场景 | 状态码 | 返回体 |
|---|---|---|
| 正常 | 200 | {"id":1} |
| 未找到 | 404 | 空 |
| 服务端错误 | 500 | 空 |
借助httptest,我们能在完全离线环境下完成HTTP客户端测试,避免了对外部网络的依赖,也显著提升了测试执行速度。