在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)) }