基於golang gin架構的單元測試

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

在用Gin架構編寫了一個web server之後,我們如果需要測試handlers介面函數的話,主要可以採用兩種方式來進行。

第一種是部署web server,然後通過瀏覽器或其他http請求類比工具來手動類比真實的http請求,發送http請求之後,解析返回的響應,查看響應是否符合預期;這種做法比較麻煩,而且測試結果不太可靠。

第二種是使用httptest結合testing來實現針對handlers介面函數的單元測試。

下面以對四個介面做相應的單元測試為例,分享基於Gin的單元測試的一些方法:

 

介面名稱

請求地址

請求類型

響應資料類型

響應資料

OnGetStringRequest

/getString

get

string

success

OnPracticeRequest

/practice

get

html頁面

practice.html

OnLoginRequestForForm

/loginForm

post

json

 

OnLoginRequestForJson

/loginJson

post

json

 

一、樣本介面代碼:

OnGetStringRequest

// OnGetStringRequest 返回success字串的介面func OnGetStringRequest(c *gin.Context) {       c.String(http.StatusOK, "success")}

OnPracticeRequest

// OnPracticeRequest 返回practice.html頁面的介面func OnPracticeRequest(c *gin.Context) {       c.HTML(http.StatusOK,"practice.html",gin.H{})}

OnLoginRequestForForm

// OnLoginRequestForForm 以表單形式傳遞參數的登入介面func OnLoginRequestForForm(c *gin.Context) {       req := &User{}       if err := c.ShouldBindWith(req, binding.Form); err != nil {              log.Printf("err:%v",err)              c.JSON(http.StatusOK, gin.H{                     "errno":"1",                     "errmsg":"參數不匹配",                     "data":"",              })              return       }       c.JSON(http.StatusOK, gin.H{              "errno":"0",              "errmsg":"",              "data":req,       })}

OnLoginRequestForJson

// OnLoginRequestForJson 以Json形式傳遞參數的登入介面func OnLoginRequestForJson(c *gin.Context) {       req := &User{}       if err := c.ShouldBindWith(req, binding.JSON); err != nil {              log.Printf("err:%v",err)              c.JSON(http.StatusOK, gin.H{                     "errno":"1",                     "errmsg":"參數不匹配",                     "data":"",              })              return       }       c.JSON(http.StatusOK, gin.H{              "errno":"0",              "errmsg":"",              "data":req,       })}

二、調用的一些結構體和工具函數:

User結構體代碼:

//承接前端傳過來的json資料或form表單資料type User struct {       Username string `form:"username" json:"username" binding:"required"`       Password   string `form:"password" json:"password" binding:"required"`       Age int `form:"age" json:"age" binding:"required"`}

LoginResponse結構體代碼:

// LoginResponse 登入介面的響應參數type LoginResponse struct {       Errno string `json:"errno"`       Errmsg string `json:"errmsg"`       Data User `json:"data"`}

調用的工具函數:

// ParseToStr 將map中的索引值對輸出成querystring形式func ParseToStr(mp map[string]string) string {       values := ""       for key, val := range mp {              values += "&" + key + "=" + val       }       temp := values[1:]       values = "?" + temp       return values}

三、單元測試編寫步驟:

1.  初始化路由

func init(){
       // 初始化路由
      
router = gin.Default()
       router.GET("/getString", OnGetStringRequest)
       router.POST("/loginForm", OnLoginRequestForForm)
       router.POST("/loginJson", OnLoginRequestForJson)
       router.LoadHTMLGlob("E:/mygo/resources/pages/*") //定義模板檔案路徑
      
router.GET("/practice", OnPracticeRequest)
}

當介面涉及到對資料庫的相關操作時,可以將資料庫服務以中介軟體的形式加到gin的Context中如所示:

2.  封裝構造http請求的函數(以便測試函數直接調用發起不同種類的http請求)

2.1構造get請求

// 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}

2.2構造post請求,以表單形式傳遞參數

// PostForm 根據特定請求uri和參數param,以表單形式傳遞參數,發起post請求返迴響應func PostForm(uri string, param map[string]string, router *gin.Engine) []byte {       // 構造post請求,表單資料以querystring的形式加在uri之後       req := httptest.NewRequest("POST", uri+ParseToStr(param), nil)       // 初始化響應       w := httptest.NewRecorder()       // 調用相應handler介面       router.ServeHTTP(w, req)       // 提取響應       result := w.Result()       defer result.Body.Close()       // 讀取響應body       body, _ := ioutil.ReadAll(result.Body)       return body}

2.3構造post請求,以Json形式傳遞參數

// PostJson 根據特定請求uri和參數param,以Json形式傳遞參數,發起post請求返迴響應func PostJson(uri string, param map[string]interface{}, router *gin.Engine) []byte {       // 將參數轉化為json位元流       jsonByte,_ := json.Marshal(param)       // 構造post請求,json資料以請求body的形式傳遞       req := httptest.NewRequest("POST", uri, bytes.NewReader(jsonByte))       // 初始化響應       w := httptest.NewRecorder()       // 調用相應的handler介面       router.ServeHTTP(w, req)       // 提取響應       result := w.Result()       defer result.Body.Close()       // 讀取響應body       body,_ := ioutil.ReadAll(result.Body)       return body}

3.  編寫測試函數

3.1針對OnGetStringRequest介面的測試函數

// TestOnGetStringRequest 測試以Get方式擷取一段字串的介面func TestOnGetStringRequest(t *testing.T) {       // 初始化請求地址       uri := "/getString"       // 發起Get請求       body := Get(uri, router)       fmt.Printf("response:%v\n", string(body))       // 判斷響應是否與預期一致       if string(body) != "success" {              t.Errorf("響應字串不符,body:%v\n",string(body))       }}

3.2針對OnPracticeRequest介面的測試函數

// TestOnPracticeRequest 測試以Get方式擷取practice.html頁面的介面func TestOnPracticeRequest(t *testing.T) {       // 初始化請求地址       uri := "/practice"       // 發起Get請求       body := Get(uri, router)       fmt.Printf("response:%v\n", string(body))       // 判斷響應是否與預期一致       html,_ := ioutil.ReadFile("E:/mygo/resources/pages/practice.html")       htmlStr := string(html)       if htmlStr != string(body) {              t.Errorf("響應資料不符,body:%v\n",string(body))       }}

3.3針對OnLoginRequestForForm介面的測試函數

// TestOnLoginRequestForForm 測試以表單形式傳遞參數的登入介面func TestOnLoginRequestForForm(t *testing.T) {       // 初始化請求地址和請求參數       uri := "/loginForm"       param := make(map[string]string)       param["username"] = "valiben"       param["password"] = "123"       param["age"] = "1"       // 發起post請求,以表單形式傳遞參數       body := PostForm(uri, param, router)       fmt.Printf("response:%v\n", string(body))       // 解析響應,判斷響應是否與預期一致       response := &LoginResponse{}       if err := json.Unmarshal(body, response); err != nil {              t.Errorf("解析響應出錯,err:%v\n",err)       }       if response.Data.Username != "valiben" {              t.Errorf("響應資料不符,username:%v\n",response.Data.Username)       }}

3.4針對OnLoginRequestForJson介面的測試函數

// TestOnLoginRequestForJson 測試以Json形式傳遞參數的登入介面func TestOnLoginRequestForJson(t *testing.T) {       // 初始化請求地址和請求參數       uri := "/loginJson"       param := make(map[string]interface{})       param["username"] = "valiben"       param["password"] = "123"       param["age"] = 1       // 發起post請求,以Json形式傳遞參數       body := PostJson(uri, param, router)       fmt.Printf("response:%v\n", string(body))       // 解析響應,判斷響應是否與預期一致       response := &LoginResponse{}       if err := json.Unmarshal(body, response); err != nil {              t.Errorf("解析響應出錯,err:%v\n",err)       }       if response.Data.Username != "valiben" {              t.Errorf("響應資料不符,username:%v\n",response.Data.Username)       }}

4.  運行單元測試,查看測試結果

執行 go test ./ 運行測試代碼,測試結果如下

四、總結

基於Gin的單元測試的主要步驟就是先要初始化路由,設定handler介面函數攔截的http請求地址(不需要設定監聽連接埠號碼);然後通過"net/http/httptest"包的NewRequest(method, target string, body io.Reader)方法構造request,第一個參數是請求類型“POST”“GET”之類,第二個參數是請求的URI地址(form表單的參數可以通過querystring的形式附在URI地址後面進行傳遞),第三個參數是請求的請求body內容(json資料等其他類型的資料可以加在這裡進行傳遞);接著通過NewRecorder()函數構造響應,調用func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request)方法來調用請求處理的介面handlers,返回的響應將寫入前面構造的響應中,通過解析響應,查看其中資料即可完成對介面的測試

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.