当前位置: 首页 > 工具软件 > HttpTest > 使用案例 >

基于gin的单元测试之httptest

龙哲
2023-12-01

目前我们的后端服务提供大量的restful api接口,每次上线都需要测试那边回归一遍这些接口,造成人力的浪费。正好借着这次单元测试和持续集成,我们引入了httptest框架,结合gin来做接口单元测试。

httptest是golang官方提供的一个包,位于/src/net/http/httptest下。

其原理的话我也看了源码研究了下,这里大致说下,它有一个ResponseRecorder结构体,它实现了http.ResponseWriter,同时它自身又包含了http.Response,前者是server端维度下的response,后者是client端维度下的response,也就是说,ResponseRecorder同时实现了server和client的功能。

好接下来看看如何使用httptest

httptest构建

 // Get 根据特定请求uri,发起get请求返回响应
func Get(uri string, router *gin.Engine) []byte {
    // 构造get请求
    req := httptest.NewRequest("GET", uri, nil)
    // 初始化响应
    w := httptest.NewRecorder()

    // 调用相应的handler接口
    router.ServeHTTP(w, req)

    // 提取响应
    result := w.Result()
    defer result.Body.Close()

    // 读取响应body
    body,_ := ioutil.ReadAll(result.Body)
    return body
}

// PostForm 根据特定请求uri和参数param,以表单形式传递参数,发起post请求返回响应
func PostForm(uri string, param url.Values, router *gin.Engine) []byte {

    // 构造post请求
    req := httptest.NewRequest("POST", uri, strings.NewReader(param.Encode()))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

    // 初始化响应
    w := httptest.NewRecorder()

    // 调用相应handler接口
    router.ServeHTTP(w, req)

    // 提取响应
    result := w.Result()
    defer result.Body.Close()

    // 读取响应body
    body, _ := ioutil.ReadAll(result.Body)
    return body
}

在gin中注册路由,并写了两个接口样例


regAdmin := router.Group("/quote")
regAdmin.GET("/ge", Ge)
regAdmin.POST("/pos", Pos)


type TestForPost struct {
    Strname string  `form:"strname" binding:"required"`
    Number int  `form:"number" binding:"required"`
}


 // Get接口
func Ge(c *gin.Context)  {
    c.String(http.StatusOK, "success")
}


 // Post接口
func Pos(c *gin.Context) {
    req := &TestForPost{}
    if err := c.ShouldBindWith(req, binding.Form); err != nil {
        fmt.Println("bind error",err)
        c.JSON(http.StatusOK, gin.H{
            "errno":"1",
            "errmsg":"参数不匹配",
            "data":"",
        })
        return
    }
    c.JSON(http.StatusOK, gin.H{
        "errno":"0",
        "errmsg":"Null",
        "data":req,
    })
}

写单元测试

type Response struct {
    Errno string `json:"errno"`
    Errmsg string `json:"errmsg"`
    Data TestForPost `json:"data"`
}

func TestOnGetStringRequest(t *testing.T) {

    // 初始化请求地址
    url:="/quote/ge"
    // 发起Get请求
    body := Get(url, router)

    // 判断响应是否与预期一致
    if string(body) != "success" {
        t.Errorf("响应字符串不符,body:%v\n",string(body))
    }

    convey.Convey("测试GET接口", t, func() {
        convey.So(string(body), convey.ShouldEqual, "success")
    })
}


func TestOnPostRequestForForm(t *testing.T) {
    // 初始化请求地址和请求参数
    uri := "/quote/pos"

    param := url.Values{
        "strname":{"test"},
        "number":{"1"},
    }
    // 发起post请求,以表单形式传递参数
    body := PostForm2(uri, param, router)
    //body := PostForm3(uri, param, router)

    // 解析响应,判断响应是否与预期一致
    response := &Response{}
    if err := json.Unmarshal(body, response); err != nil {
        t.Errorf("解析响应出错,err:%v\n",err)
    }
    t.Log(response.Data)
    if response.Data.Strname != "test" {
        t.Errorf("响应数据不符,errmsg:%v, data:%v\n",response.Errmsg,response.Data.Strname)
    }
    convey.Convey("测试POST接口", t, func() {
        convey.So(response.Data.Strname, convey.ShouldEqual, "test")
    })
}

完成

 类似资料: