You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gin接口测试时httptest ResponseRecorder保留旧值导致单测失败

你的问题和测试并行化无关,核心原因是多个子测试复用了同一个httptest.ResponseRecorder实例,且错误使用了Flush方法重置状态。

具体原因

  • httptest.ResponseRecorder的Flush()方法仅负责清空内部缓冲的响应体,不会重置已经记录的状态码、响应头数据,前一个测试用例写入的状态码会一直保留到后一个用例中,才会出现后一个用例逻辑走通但状态码还是前一个值的现象。
  • 此外你将gin路由上下文、mock实例、全局变量赋值都定义在了子测试外层,多个用例共用这些实例也存在状态污染的风险,比如全局的client、session如果被某个用例修改,就会影响后续用例执行。

修复方案

将每个子测试需要独立的资源全部移到子测试内部初始化,不要跨用例复用:

func TestHandlerAuthentication(t *testing.T) {
    UrlOdoo = "https://isi.nc"
    gin.SetMode(gin.TestMode)

    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    // 通用mock规则可以放在外层
    client = mock_odoorpc.NewMockOdooClient(ctrl)
    client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), invalidUsername, invalidPassword).AnyTimes().Return(fmt.Errorf("invalid login"))
    client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), validUsername, validPassword).AnyTimes().Return(nil)
    client.EXPECT().Logout(gomock.Any()).AnyTimes().Return(nil)

    session = mock_session.NewMockSession(ctrl)
    Session = SessionGetter{createMockSession}
    session.EXPECT().Set("username", validUsername).AnyTimes().Return()
    session.EXPECT().Set("password", validPassword).AnyTimes().Return()
    session.EXPECT().Save().AnyTimes().Return(nil)
    Odoo = OdooRPC{createMockOdooClient}

    for name, test := range map[string]struct {
        input   Login
        want    int
    }{
        "valid login": {
            input: Login{
                Username: validUsername,
                Password: validPassword,
            },
            want: 200,
        },
        "invalid login": {
            input: Login{
                Username: invalidUsername,
                Password: invalidPassword,
            },
            want: 401,
        },
    } {
        t.Run(name, func(t *testing.T) {
            // 每个子测试单独创建独立的响应记录器、gin上下文和路由
            resp := httptest.NewRecorder()
            c, r := gin.CreateTestContext(resp)
            r.POST("/test", handlerAuthentication)
            
            body, _ := json.Marshal(test.input)
            c.Request, _ = http.NewRequest(http.MethodPost, "/test", strings.NewReader(string(body)))
            r.ServeHTTP(resp, c.Request)
            assert.Equal(t, test.want, resp.Code)
        })
    }
}

补充说明

如果确实需要复用ResponseRecorder,不要用Flush,应该每次用例执行前手动重置所有状态:

resp.Code = http.StatusOK
resp.HeaderMap = http.Header{}
resp.Body = &bytes.Buffer{}
resp.Flushed = false

更推荐每个子测试单独创建实例,避免遗漏重置项导致的隐藏问题。

内容的提问来源于stack exchange,提问作者Couldosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 12:24:05