基於golang gin框架的單元測試

在用Gin框架編寫了一個web server以後,咱們若是須要測試handlers接口函數的話,主要能夠採用兩種方式來進行。git

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

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

github項目地址:https://github.com/Valiben/gin_unit_testjson

下面以一個簡單的登陸handler爲例子,來講明基於Gin框架的單元測試的方法。瀏覽器

首先定義接口處理函數:框架

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"`
}

func LoginHandler(c *gin.Context) {
   req := &User{}
   if err := c.Bind(req); err != nil {
      log.Printf("err:%v", err)
      c.JSON(http.StatusOK, gin.H{
         "errno":  "1",
         "errmsg": "parameters not match",
      })
      return
   }

   // judge the password and username
   if req.UserName != "Valiben" || req.Password != "123456" {
      c.JSON(http.StatusOK, gin.H{
         "errno":  "2",
         "errmsg": "password or username is wrong",
      })
      return
   }

   c.JSON(http.StatusOK, gin.H{
      "errno":  "0",
      "errmsg": "login success",
   })
}

接下來,在單元測試文件中導入上述包:函數

import utils "github.com/Valiben/gin_unit_test"

搭建路由,將engine設置到utils中:工具

router := gin.Default()
router.POST("/login", LoginHandler)
utils.SetRouter(router)

設置好engine以後你就能夠像下面同樣編寫一個單元測試用例來測試登陸接口了:單元測試

type OrdinaryResponse struct {
   Errno  string `json:"errno"`
   Errmsg string `json:"errmsg"`
}

func TestLoginHandler(t *testing.T) {
   resp := OrdinaryResponse{}
   
   err := utils.TestHandlerUnMarshalResp(utils.POST, "/login", utils.Form, user, &resp)
   if err != nil {
      t.Errorf("TestLoginHandler: %v\n", err)
      return
   }
   
   if resp.Errno != "0" {
      t.Errorf("TestLoginHandler: response is not expected\n")
      return
   }
}

而後就能夠運行這個單元測試來檢驗接口函數啦,是否是很簡單呢。測試

若是是上傳文件之類的接口的單元測試,怎麼來寫呢,假設上傳文件的接口函數以下:

type FileRequest struct {
   FileName   string `json:"file_name" form:"file_name" binding:"required"`
   UploadName string `json:"upload_name" form:"upload_name" binding:"required"`
}

func SaveFileHandler(c *gin.Context) {

   req := &FileRequest{}

   if err := c.Bind(req); err != nil {
      log.Printf("err:%v", err)
      c.JSON(http.StatusOK, gin.H{
         "errno":  "1",
         "errmsg": "parameters not match",
      })
      return
   }

   // get the file of the request
   file, _, _ := c.Request.FormFile("file")
   if file == nil {
      c.JSON(http.StatusOK, gin.H{
         "errno":  "2",
         "errmsg": "file is nil",
      })
      return
   }

   fmt.Printf("SaveFile: req:%+v\n", req)
   out, err := os.Create("test2.txt")

   if err != nil {
      c.JSON(http.StatusOK, gin.H{
         "errno":  "2",
         "errmsg": err.Error(),
      })
      return
   }

   // copy the content of the file to the out
   _, err = io.Copy(out, file)
   defer file.Close()
   defer out.Close()

   if err != nil {
      c.JSON(http.StatusOK, gin.H{
         "errno":  "2",
         "errmsg": err.Error(),
      })
      return
   }

   c.JSON(http.StatusOK, gin.H{
      "errno":  "0",
      "errmsg": "save file success",
   })
}

再把接口函數設置到路由中,那麼單元測試就能夠這樣來寫啦:

func TestSaveFileHandler(t *testing.T) {
   param := make(map[string]interface{})
   param["file_name"] = "test1.txt"
   param["upload_name"] = "Valiben"

   resp := OrdinaryResponse{}
   err := utils.TestFileHandlerUnMarshalResp(utils.POST, "/upload", (param["file_name"]).(string),
      "file", param, &resp)
   if err != nil {
      t.Errorf("TestSaveFileHandler: %v\n", err)
      return
   }

   if resp.Errno != "0" {
      t.Errorf("TestSaveFileHandler: response is not expected\n")
      return
   }
}

file_name是文件路徑名,能夠是相對路徑也能夠是絕對路徑,該測試函數模擬調用/upload請求,上傳當前路徑下的test1.txt。

除此以外,小demo還支持自定義請求頭(諸如jwt-token等),更多用法能夠看小demo中的test/handlers_test哦

相關文章
相關標籤/搜索