在用Gin框架编写了一个web server以后,咱们若是须要测试handlers接口函数的话,主要能够采用两种方式来进行。git
第一种是部署web server,而后经过浏览器或其余http请求模拟工具来手动模拟真实的http请求,发送http请求以后,解析返回的响应,查看响应是否符合预期;这种作法比较麻烦,并且测试结果不太可靠。github
第二种是使用httptest结合testing来实现针对handlers接口函数的单元测试。web
下面以一个简单的登陆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哦