在用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哦