Go测试中如何构造请求和响应_HTTP测试示例说明

13次阅读

httptest.NewRequest用于构造内存请求测试handler,需设method、URL、body及Content-Type;NewRecorder捕获响应供断言;路由参数须手动注入context;客户端测试应使用NewUnstartedServer而非NewRecorder。

Go测试中如何构造请求和响应_HTTP测试示例说明

go测试中如何用httptest.NewRequest构造请求

直接调用http.HandlerFunchttp.Handler时,不能传入真实网络请求,必须用httptest.NewRequest生成可修改的*http.Request。它不触发dns、连接或TLS,纯内存操作,安全可靠。

常见错误是忽略body类型和Content-Type头不匹配,导致r.ParseForm()json.NewDecoder(r.Body).Decode()失败。

  • methodurl必须非空,否则NewRequest panic
  • POST/PUT请求若带json数据,需显式设置Content-Type: application/json
  • 表单数据要用Strings.NewReader("key=value&other=1")并设application/x-www-form-urlencoded
  • 路径含查询参数时,推荐用url.Parse拼接,避免手动编码出错
req := httptest.NewRequest("POST", "/api/users", strings.NewReader(`{"name":"alice"}`)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer abc123")

如何用httptest.NewRecorder捕获响应

httptest.NewRecorder()返回一个实现了http.ResponseWriter接口的记录器,所有写入(状态码、header、body)都会被缓存,不发送到网络。这是验证 handler 行为的核心工具

注意:recorder.Body.String()只能在 handler 执行完后调用;提前读会得到空字符串。另外recorder.Codeint 类型,不是字符串,别误用strconv.Itoa转换后再比较。

  • 响应体是*bytes.Buffer,支持Bytes()String()Len()
  • recorder.Header()返回的是http.Header副本,修改它不影响已写入的响应头
  • 若 handler 调用了http.Errorpanic,仍可通过recorder.Coderecorder.Body断言结果
rr := httptest.NewRecorder() handler := http.HandlerFunc(yourHandler) handler.ServeHTTP(rr, req)  if rr.Code != http.StatusCreated {     t.Errorf("expected status %d, got %d", http.StatusCreated, rr.Code) } if !strings.Contains(rr.Body.String(), `"name":"alice"`) {     t.Errorf("response body doesn't contain expected JSON") }

测试带路由参数的HTTP handler(如gorilla/mux或chi)

第三方路由器(如gorilla/mux)依赖request.Context()中的变量,单纯用httptest.NewRequest无法自动注入URLVars。必须手动把变量塞进context,再赋给req

漏掉这步会导致mux.Vars(r)返回空 map,handler 读不到:id等参数,逻辑直接跳过或 panic。

  • 使用chi.URLParam时同理,需用chi.WithValue包装req
  • 不要试图修改req.URL.Path来“欺骗”路由——中间件和路由匹配早已完成
  • 如果 handler 内部调用r.Context().Value(),也要提前注入对应 key
// gorilla/mux 示例 r := mux.NewRouter() r.HandleFunc("/users/{id}", userHandler).Methods("GET")  req := httptest.NewRequest("GET", "/users/123", nil) rr := httptest.NewRecorder()  // 手动注入 URLVars vars := map[string]string{"id": "123"} req = mux.SetURLVars(req, vars)  r.ServeHTTP(rr, req)

为什么不能直接用net/http/httptest测客户端代码

httptest专为测试http.Handler设计,不模拟服务端监听或网络往返。如果你在测 HTTP 客户端(比如用http.Client发请求),应该用httptest.NewUnstartedServer或更推荐的gockhttpmock等库拦截 outbound 请求。

常见误用:用NewRecorder去“接收”客户端发的请求——这是方向反了。客户端需要的是 mock server,不是 recorder。

  • NewUnstartedServer返回*httptest.Server,可调用Start()启动,URL属性即 mock 地址
  • ts.URL替换客户端配置中的 base URL,即可把真实请求导向本地 mock
  • 务必在测试结束调用ts.Close(),否则端口泄漏,后续测试可能失败
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {     w.WriteHeader(http.StatusOK)     w.Write([]byte(`{"ok":true}`)) })) ts.Start() defer ts.Close()  // 然后配置 client.BaseURL = ts.URL // 再调用 client.Do(...) —— 请求将打到 ts

测试 HTTP handler 的关键不在“写得多”,而在“控制得准”:请求能带齐 header/body/context,响应能精确断言 Code/Body/Header。最容易被忽略的是路由参数注入和客户端/服务端测试场景混淆——前者不填URLVars就永远拿不到:id,后者用错NewRecorder就根本测不到真实行为。

text=ZqhQzanResources