Gin框架SetCookie未修改旧Cookie却新增,测试失败求助
我正在测试Gin框架中包含ctx.SetCookie方法的Logout处理器:
func (a *authController) Logout(ctx *gin.Context) { refreshToken, err := ctx.Cookie("refresh_token") ... // 设置空值、MaxAge=-1来删除Cookie ctx.SetCookie("access_token", "", -1, "/", "localhost", false, true) ctx.SetCookie("refresh_token", "", -1, "/", "localhost", false, true) ctx.SetCookie("logged_in", "", -1, "/", "localhost", false, true) ctx.JSON(http.StatusOK, gin.H{"status": "success"}) }
测试函数代码如下:
recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.SetCookie("logged_in", "truee", 60*60, "/", "localhost", false, false) req, _ := http.NewRequest("GET", "/logout", nil) // 错误地用recorder设置请求Cookie http.SetCookie(recorder, &http.Cookie{Name: "refresh_token", Value: "encodedRefreshToken", MaxAge: 60 * 60, Path: "/", Domain: "localhost", Secure: false, HttpOnly: true}) http.SetCookie(recorder, &http.Cookie{Name: "access_token", Value: "encodedAccessToken", MaxAge: 60 * 60, Path: "/", Domain: "localhost", Secure: false, HttpOnly: true}) req.Header = http.Header{"Cookie": recorder.Result().Header["Set-Cookie"]} ctx.Request = req test.mock() authController.Logout(ctx)
调用后检查Cookie是否删除时,发现SetCookie并未修改原有Cookie,而是新增了同名Cookie,导致存在两对access_token等Cookie,测试不通过:
coockies := recorder.Result().Cookies() for _, c := range coockies { if c.Name == "access_token" { assert.Equal(t, "", c.Value) } ... }
错误原因
你误用了httptest.Recorder来构建请求的Cookie。httptest.Recorder的作用是记录服务器的响应,而非构造请求的Cookie。当你调用http.SetCookie(recorder, ...)时,实际上是在给响应的Set-Cookie头部添加内容,随后你把这些响应头当成请求的Cookie头传入,导致请求本身携带了这些Cookie;而Logout处理器又添加了删除Cookie的Set-Cookie指令,最终响应里会同时存在原有Cookie(来自错误的请求头构造)和删除指令的Cookie,出现重复。
另外,ctx.SetCookie("logged_in", ...)在测试上下文中的调用也不会作用到请求上——此时ctx的Request还未被赋值,这个操作完全无效。
解决方案
直接在http.Request的Header中正确设置Cookie,或者使用http.SetCookie给请求添加Cookie(注意是给请求的Header添加,而非给Recorder)。
修正后的测试代码:
import "strings" // ... recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) req, _ := http.NewRequest("GET", "/logout", nil) // 构建请求的Cookie头部 var cookieSegments []string // 添加access_token Cookie accessCookie := &http.Cookie{Name: "access_token", Value: "encodedAccessToken", MaxAge: 60 * 60, Path: "/", Domain: "localhost", Secure: false, HttpOnly: true} cookieSegments = append(cookieSegments, accessCookie.String()) // 添加refresh_token Cookie refreshCookie := &http.Cookie{Name: "refresh_token", Value: "encodedRefreshToken", MaxAge: 60 * 60, Path: "/", Domain: "localhost", Secure: false, HttpOnly: true} cookieSegments = append(cookieSegments, refreshCookie.String()) // 添加logged_in Cookie loggedInCookie := &http.Cookie{Name: "logged_in", Value: "truee", MaxAge: 60 * 60, Path: "/", Domain: "localhost", Secure: false, HttpOnly: false} cookieSegments = append(cookieSegments, loggedInCookie.String()) // 设置请求的Cookie头部 req.Header.Set("Cookie", strings.Join(cookieSegments, "; ")) ctx.Request = req test.mock() authController.Logout(ctx) // 检查响应中的Cookie cookies := recorder.Result().Cookies() for _, c := range cookies { switch c.Name { case "access_token", "refresh_token", "logged_in": assert.Equal(t, "", c.Value) assert.Equal(t, -1, c.MaxAge) } }
关键修正点
- 放弃用
httptest.Recorder构造请求Cookie,直接操作http.Request的Header。 - 使用
Cookie.String()方法将Cookie对象转换为符合HTTP Cookie头部格式的字符串,再用;拼接多个Cookie。 - 测试时不仅检查Cookie的Value为空,还要验证
MaxAge=-1——这是告诉浏览器删除Cookie的标准方式,比仅设置空值更可靠。
内容的提问来源于stack exchange,提问作者Omegon
相关产品推荐
相关产品推荐

