在Test
中在模擬接口測試,首先咱們先實現一個最基礎的Test
例子:json
模擬一個ping/pong
的最基本請求,咱們先寫一個返回pong
的HTTP handler
app
import ( "io" "net/http" "net/http/httptest" "testing" ) func Pong(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "text/plain") io.WriteString(w, "pong") }
而後寫測試用例:框架
func TestRequest(t *testing.T) { req, err := http.NewRequest("GET", "ping", nil) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() handler := http.HandlerFunc(Pong) handler.ServeHTTP(rr, req) if status := rr.Code; status != http.StatusOK { t.Errorf("status code %v", rr.Code) } if rr.Body.String() != "pong" { t.Errorf("returned %s", rr.Body.String()) } }
程序日誌輸出Pass
,這個小demo
正常運行了。而後咱們在這個基礎上,咱們給請求增長一個超時時間、以及攜帶header
頭等信息測試
咱們將請求的header
頭返回,處理的hander
以下:ui
func GetUsersHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") output, _ := json.Marshal(r.Header) io.WriteString(w, string(output)) }
而後是咱們的測試用例, 並且也更貼近咱們的真實開發:日誌
func TestRequest(t *testing.T) { req, _ := http.NewRequest("GET", "/user/info", nil) // 設置header頭 req.Header.Set("uid", "10086") rr := httptest.NewRecorder() handler := http.HandlerFunc(GetUsersHandler) // 給請求設置1s的超時 ctx := req.Context() ctx, _ = context.WithTimeout(ctx, time.Second) req = req.WithContext(ctx) handler.ServeHTTP(rr, req) if status := rr.Code; status != http.StatusOK { t.Errorf("status code %v", rr.Code) } t.Log(rr.Body.String()) }
而後咱們追加一個middleware
來讓代碼更加真實,middleware
的做用就是在context
中設置auth
的結果。code
// 中間件,在context中設置一個auth func MiddlewareHandler(h http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), "auth", "middle verify") h.ServeHTTP(w, r.WithContext(ctx)) } return http.HandlerFunc(fn) }
連帶的對測試的方法作一下簡單的調整:orm
func TestRequest(t *testing.T) { req, _ := http.NewRequest("GET", "/user/info", nil) // 設置header頭 req.Header.Set("uid", "10086") // 設置middleware rr := httptest.NewRecorder() handler := MiddlewareHandler(http.HandlerFunc(GetUsersHandler)) // 給請求設置1s的超時 ctx, _ := context.WithTimeout(req.Context(), time.Second) req = req.WithContext(ctx) handler.ServeHTTP(rr, req) if status := rr.Code; status != http.StatusOK { t.Errorf("status code %v", rr.Code) } t.Log(rr.Body.String()) }
最後是將經過middleware
傳遞的context
值打印輸出:router
func GetUsersHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") output, _ := json.Marshal(r.Context().Value("auth")) io.WriteString(w, string(output)) }
繼續按照上面的方法,咱們使用gin
框架在作一次嘗試。接口/user/info
經過Bind
的方式來獲取參數。server
咱們在方法體內聲明告終構體,同時將類型定義爲interface
。當收到客戶端傳遞的參數時,解析到變量r
上,最後json格式打印輸出
func TestRequest(t *testing.T) { req, _ := http.NewRequest("GET", "/user/info?type=1", nil) // 啓動一個Gin的接口 router := gin.Default() router.GET("/user/info", func(c *gin.Context) { type request struct { Type interface{} `json:"type" form:"type"` } r := &request{} if err := c.Bind(r); err != nil { t.Fatal(err) } b, _ := json.Marshal(r) t.Log(string(b)) }) // 使用Gin的服務 rr := httptest.NewRecorder() router.ServeHTTP(rr, req) }
在使用gin
進行綁定的時候,返回了錯誤Unknown type
。
咱們對代碼進行簡單的封裝,這樣就能夠提供一個統一的測試方法,咱們對服務進行封裝
// 將sever的部分抽象出來 func server(w *httptest.ResponseRecorder, r *http.Request) { router := gin.Default() router.GET("/user/info", func(c *gin.Context) { type request struct { Type interface{} `json:"type" form:"type"` } r := &request{} if err := c.Bind(r); err != nil { c.JSON(http.StatusBadRequest, gin.H{"type": 0}) return } b, _ := json.Marshal(r) c.JSON(http.StatusOK, gin.H{"type": string(b)}) }) router.ServeHTTP(w, r) }
咱們對請求進行封裝:
func TestRequest(t *testing.T) { req, _ := http.NewRequest("GET", "/user/info?type=1", nil) // 使用Gin的服務 rr := httptest.NewRecorder() server(rr, req) p, err := ioutil.ReadAll(rr.Body) if err != nil { t.Fatal(err) } t.Log(string(p)) }