如何在Go中测试响应体?解决测试时w.Body为空的问题
正确测试返回JSON响应的HTTP处理函数
看起来你是在Go里测试HTTP处理函数对吧?我之前也踩过一模一样的坑——运行时能正常拿到JSON响应,测试时w.Body却为空,问题肯定出在测试流程上,不是你的业务函数本身。结合你处理自定义结构体c的场景,给你一步步拆解正确的测试方式:
1. 先搭好测试的基础架子
首先得用Go标准库的net/http/httptest包,用httptest.ResponseRecorder来模拟HTTP响应Writer,再构造符合你函数预期的请求。举个贴合你场景的例子:
// 假设你的自定义结构体是这样的 type c struct { Name string `json:"name"` Age int `json:"age"` } // 你的业务处理函数(大概长这样) func CustomHandler(w http.ResponseWriter, r *http.Request) { // 生成自定义结构体实例 data := c{Name: "测试数据", Age: 30} // 返回JSON响应 w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(data) } // 对应的测试函数 func TestCustomHandler(t *testing.T) { // 1. 创建模拟的ResponseRecorder,用来捕获响应 rr := httptest.NewRecorder() // 2. 构造测试请求(根据你的实际需求调整方法、路径、请求体) req, err := http.NewRequest("GET", "/your-path", nil) if err != nil { t.Fatalf("构造请求失败: %v", err) } // 3. 调用你的处理函数,把模拟的Writer和请求传进去 CustomHandler(rr, req)
2. 读取响应体的关键步骤(别踩坑!)
很多人就是在这里出错,直接读rr.Body却忽略了细节。正确的流程是:
// 先检查响应状态码是否符合预期 if rr.Code != http.StatusOK { t.Errorf("状态码不对: 预期%d, 实际%d", http.StatusOK, rr.Code) } // 检查Content-Type是否是JSON类型 expectedContentType := "application/json; charset=utf-8" if rr.Header().Get("Content-Type") != expectedContentType { t.Errorf("Content-Type不对: 预期%s, 实际%s", expectedContentType, rr.Header().Get("Content-Type")) } // 读取响应体的正确方式: // 方式一:直接用String()获取(最简单) bodyContent := rr.Body.String() // 方式二:如果需要处理字节,用io.ReadAll,注意读完后可以把指针移回开头复用 // bodyBytes, err := io.ReadAll(rr.Body) // if err != nil { // t.Fatalf("读取响应体失败: %v", err) // } // _ = rr.Body.Seek(0, io.SeekStart) // 把指针移回开头,方便后续复用 // 验证响应体是否和预期一致(比如和自定义结构体序列化后的JSON对比) expectedData := c{Name: "测试数据", Age: 30} expectedJson, err := json.Marshal(expectedData) if err != nil { t.Fatalf("序列化预期数据失败: %v", err) } // 注意:json.Encode会在末尾加换行,所以用TrimSpace去掉首尾空白再对比 if strings.TrimSpace(bodyContent) != strings.TrimSpace(string(expectedJson)) { t.Errorf("响应体不对: 预期%q, 实际%q", string(expectedJson), bodyContent) } }
3. 排查你可能犯的错误
- 没触发函数里的JSON写入逻辑:比如你的函数里有分支判断(比如需要特定请求参数、请求头),测试时没满足条件,导致根本没往Writer里写内容
- 读取Body时指针位置不对:如果之前读过一次
rr.Body,指针会停在末尾,再次读就会得到空字符串,这种情况用rr.Body.Seek(0, io.SeekStart)把指针移回开头就行 - 用了异步写入:如果你的函数里是用goroutine异步写响应,测试会直接结束,导致
rr.Body还没被写入。这种情况要改成同步写入,或者在测试里加等待逻辑
4. 额外小技巧:用断言库简化测试
如果觉得手动写判断太繁琐,可以用testify/assert这类断言库,让测试代码更简洁易读:
import "github.com/stretchr/testify/assert" func TestCustomHandler(t *testing.T) { rr := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/your-path", nil) CustomHandler(rr, req) // 用断言简化判断 assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, "application/json; charset=utf-8", rr.Header().Get("Content-Type")) // 直接把响应体反序列化成自定义结构体,再对比 var actualData c err := json.NewDecoder(rr.Body).Decode(&actualData) assert.NoError(t, err) assert.Equal(t, c{Name: "测试数据", Age: 30}, actualData) }
内容的提问来源于stack exchange,提问作者Vivi
相关产品推荐
相关产品推荐

