在使用Go語言編寫Web應用程序時,我們經常會用到gin框架來處理HTTP請求和響應。在進行單元測試時,我們需要對代碼進行覆蓋測試,以保證代碼的質量和穩定性。然而,針對gin的Context.Redirect方法的單元測試在處理GET請求時非常適用,但在處理POST請求時卻不太適用。在本文中,php小編蘋果將會詳細解釋為什么這個問題會出現,并提供一些解決方案來進行POST請求的單元測試。
問題內容
我希望我的服務器將特定端點重定向到另一臺服務器。該端點可以是 get
ted 或 post
ed。在這兩種情況下,http 響應代碼都應為 302。如果我在此代碼上使用 curl
,它確實在兩種情況下都顯示響應代碼 302,并且 curl -l
正確遵循重定向。哇哦。
但是
我的單元測試使用httptest.newrecorder()
來捕獲信息,但它僅適用于get
,不適用于post
。因此,當我知道實際的重定向正在工作時,我需要弄清楚如何讓單元測試工作。失敗測試顯示http響應代碼是200而不是302(http.statusfound
)。
$ go run foo.go post code 200 get code 302
登錄后復制
這是獨立測試。
package main import ( "net/http" "net/http/httptest" "github.com/gin-gonic/gin" ) func main() { gin.setmode(gin.releasemode) { w := httptest.newrecorder() context, _ := gin.createtestcontext(w) context.request = httptest.newrequest("post", "http://localhost:23632/foobar", nil) context.redirect(http.statusfound, "http://foobar.com") print("post code ",w.code,"\n") } { w := httptest.newrecorder() context, _ := gin.createtestcontext(w) context.request = httptest.newrequest("get", "http://localhost:23632/foobar", nil) context.redirect(http.statusfound, "http://foobar.com") print("get code ",w.code,"\n") } }
登錄后復制
當我在實際應用程序(未顯示)上執行 curl post 時,我發現它正在工作:
curl -v -XPOST localhost:23632/foobar * About to connect() to localhost port 23632 (#0) * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 23632 (#0) > POST /foobar HTTP/1.1 > User-Agent: curl/7.29.0 > Host: localhost:23632 > Accept: */* > < HTTP/1.1 302 Found < Location: http://foobar.com < Vary: Origin < Date: Tue, 23 May 2023 22:38:42 GMT < Content-Length: 0 < * Connection #0 to host localhost left intact
登錄后復制
解決方法
tl;dr
解決方法是在 context.redirect
之后顯式調用 context.writer.writeheadernow
。
說明
這是使用從 gin.createtestcontext
返回的 gin 上下文的一個極端情況。
對于 get 請求,gin 最終會調用 http.redirect
,它將向響應寫入一個簡短的 html 正文(類似于 found
),從而導致要寫入響應的狀態代碼。
對于 post 請求,http.redirect
不會寫入短 html 正文,并且狀態代碼沒有機會寫入響應。
參見http 的實現.重定向。根據源碼,如果之前設置了content-type
header,那么get請求也會出現同樣的問題:
{ w := httptest.newrecorder() context, _ := gin.createtestcontext(w) context.request = httptest.newrequest("get", "http://localhost:23632/foobar", nil) + context.header("content-type", "text/html") context.redirect(http.statusfound, "http://foobar.com") print("get code ", w.code, "\n") }
登錄后復制
解決方法是顯式調用 context.writer.writeheadernow
:
{ w := httptest.NewRecorder() context, _ := gin.CreateTestContext(w) context.Request = httptest.NewRequest("POST", "http://localhost:23632/foobar", nil) context.Redirect(http.StatusFound, "http://foobar.com") + context.Writer.WriteHeaderNow() print("POST code ", w.Code, "\n") }
登錄后復制
gin 本身使用相同的解決方法。請參閱 testcontextrenderredirectwithrelativepath。
真正的服務器應用程序不會遇到同樣的問題,因為 (*engine).handlehttprequest
將為我們調用 writeheadernow
(請參閱 源代碼)。這就是為什么我稱其為“極端情況”而不是“錯誤”。