这里的博客版本都不会被更新维护。查看最新的版本请移步:
http://neojos.com
Gin
对net/http
包做了封装,支持路由、中间件等特性,极大的方便对Http Server
的开发。文章通过一个Test
例子,来简要介绍。对于特别基础的部分,请阅读参考文章。
接口测试
Go
中testing
包为程序自测提供了便利。可以查阅之前写的博客Go test基础用法
,对其内容,我还是挺满意的。
使用Postman
对于接口测试,很多情况都在使用Postman
这样的工具。首先在本地启动服务,然后在Postman
中配置请求的地址和参数、执行请求、查看结果。
这种方式唯一让人不满意的地方在于:每次修改都需要重启服务。跟直接执行一次Test
相比,明显多了一步。
使用Test
测试基类
下面的代码作为接口测试的基类。
TestMain
中,我们为所有的测试用例指定通用的配置。之后在执行其他Test
前,都会先执行TestMain
中的代码。有效的避免了代码冗余。
getRouter
方法用于返回一个gin
的实例。我们将服务的路由重新在Test
中指定,并设置了中间件。
testHttpResponse
是我们请求处理的核心代码,发送请求,并保存响应到w
中
//common_test.go
func TestMain(m *testing.M) {
//声明执行Test前的操作
gin.SetMode(gin.TestMode)
testutils.NewTestApp("../conf.test.toml")
flag.Parse()
os.Exit(m.Run())
}
//设置路由,获取框架Gin的实例
func getRouter() *gin.Engine {
router := gin.Default()
//配置路由,这是我项目中的自定义配置
router.Use(middleware.HeaderProcess())
RouteAPI(router)
return router
}
//统一处理请求返回结果
func testHttpResponse(t *testing.T, r *gin.Engine, req *http.Request, f func(w *httptest.ResponseRecorder) error) {
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if err := f(w); err != nil {
t.Fatal(err)
}
}
测试用例
下面是具体的测试用例。我们构造了一个Json
数据格式的POST
请求,然后通过调用testHttpResponse
方法来读取接口的响应数据。
关于NewRequest
方法,它参数body
传递一个io.Reader
接口类型。从源代码可以看出,实现了该接口的分别是:bytes.Buffer
、bytes.Reader
、strings.Reader
。
func TestWeChatRecharge(t *testing.T) {
router := getRouter()
//构造json的请求体
params := map[string]interface{}{
"open_id": "olFg1s3gPcISnooRX9WSkX_E-cww",
"device_type": "ANDROID",
}
jsonParams, _ := json.Marshal(params)
readParams := bytes.NewReader(jsonParams)
req, _ := http.NewRequest("POST", "/pay/wx/recharge", readParams)
req.Header.Set("Content-type", "application/json")
//发送请求
testHttpResponse(t, router, req, func(w *httptest.ResponseRecorder) error {
p, err := ioutil.ReadAll(w.Body)
if err != nil {
return err
}
t.Logf("%+v", string(p))
return nil
})
}
小结
通过上述的步骤,我们实现了直接在Test
中做接口测试。
Middleware
声明一个middleware
函数,返回类型为type HandlerFunc func(*Context)
。
func setUserStatus() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("set status")
}
}
如果需要将函数应用于所有的请求,使用Use
方法。比如统一的请求头转换、错误输出、日志打印等
//Use adds middleware to the group
router.Use(setUserStatus())
下面是给具体的请求设置中间件。从这里可以看出,中间件处理函数和正常的业务处理函数类型是相同的。
//Use a particular middleware
articleRoutes.GET("/create", setUserStatus(), showArticleCreationPage)
最后系统依次调用注册的handler
完成请求处理:
func (c *Context) Next() {
c.index++
for s := int8(len(c.handlers)); c.index < s; c.index++ {
c.handlers[c.index](c)
}
}
参考文章: