go语言单元测试之二gin框架用httptest做单元测试
目录
go语言单元测试之二:gin框架用httptest做单元测试
一,安装用到的库
1,testify的代码地址:
2,从命令行安装:
liuhongdi@ku:~$ go get -u github.com/stretchr/testify
我们可以用testify的assert模块简化代码
说明:刘宏缔的go森林是一个专注golang的博客,
网站:
原文:
说明:作者:刘宏缔 邮箱:
二,演示项目的相关信息
1,地址:
2,功能说明:演示了用httptest做gin框架中接口(controller或称handler)的测试
3,项目结构:如图:
三,go代码说明
1,controller/userController.go
package controller
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/liuhongdi/gintest01/global"
"strconv"
)
//post struct
type TestForPost struct {
Username string `form:"username" binding:"required,min=6"`
Password string `form:"password" binding:"required,min=6,max=12"`
}
type UserController struct{}
func NewUserController() UserController {
return UserController{}
}
// Get接口
func (g *UserController) GetUser(c *gin.Context) {
res := global.NewResult(c)
uid := c.DefaultQuery("uid", "0")
uidint, err := strconv.ParseInt(uid, 10, 64)
if (err!=nil) {
res.Error(1,"用户id需要为整数")
return
}
if (uidint <= 0) {
res.Error(2,"用户id<=0")
return
}
res.Success("success")
return
}
// Post接口
func (g *UserController) PostUser(c *gin.Context) {
res := global.NewResult(c)
req := &TestForPost{}
if err := c.ShouldBindWith(req, binding.Form); err != nil {
//fmt.Println("bind error",err)
res.Error(1,"参数不匹配")
return
}
res.Success("success")
}
2,global/result.go
package global
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Result struct {
Ctx *gin.Context
}
//返回结果集的格式
type ResultCont struct {
Code int `json:"code"` // 代码
Msg string `json:"msg"` //提示信息
Data interface{} `json:"data"` //数据
}
func NewResult(ctx *gin.Context) *Result {
return &Result{Ctx: ctx}
}
//成功
func (r *Result) Success(data interface{}) {
if (data == nil) {
data = gin.H{}
}
res := ResultCont{}
res.Code = 0
res.Msg = ""
res.Data = data
r.Ctx.JSON(http.StatusOK,res)
}
//报错
func (r *Result)Error(code int,msg string) {
res := ResultCont{}
res.Code = code
res.Msg = msg
res.Data = ""
r.Ctx.JSON(http.StatusOK,res)
r.Ctx.Abort()
}
3,pkg/unittest/httptest.go
package unittest
import (
"github.com/gin-gonic/gin"
"net/http/httptest"
)
//map转字符串
func ParseToStr(mp map[string]string) string {
values := ""
for key, val := range mp {
values += "&" + key + "=" + val
}
temp := values[1:]
values = "?" + temp
return values
}
//get access controller
func Get(uri string, router *gin.Engine) *httptest.ResponseRecorder {
// 构造get请求
req := httptest.NewRequest("GET", uri, nil)
// 初始化响应
w := httptest.NewRecorder()
// 调用相应的handler接口
router.ServeHTTP(w, req)
return w
}
//post access controller
func PostForm(uri string, param map[string]string, router *gin.Engine) *httptest.ResponseRecorder {
req := httptest.NewRequest("POST", uri+ParseToStr(param), nil)
// 初始化响应
w := httptest.NewRecorder()
// 调用相应handler接口
router.ServeHTTP(w, req)
return w
}
4,router/router.go
package router
import (
"github.com/gin-gonic/gin"
"github.com/liuhongdi/gintest01/controller"
"github.com/liuhongdi/gintest01/global"
"log"
"runtime/debug"
)
func Router() *gin.Engine {
router := gin.Default()
//处理异常
router.NoRoute(HandleNotFound)
router.NoMethod(HandleNotFound)
router.Use(Recover)
// 路径映射 ;
userc:=controller.NewUserController()
router.GET("/user/get", userc.GetUser);
router.POST("/user/post", userc.PostUser);
return router
}
func HandleNotFound(c *gin.Context) {
global.NewResult(c).Error(404,"资源未找到")
return
}
func Recover(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
//打印错误堆栈信息
log.Printf("panic: %v\n", r)
debug.PrintStack()
global.NewResult(c).Error(500,"服务器内部错误")
}
}()
//加载完 defer recover,继续后续接口调用
c.Next()
}
5,main_test.go
package main
import (
"encoding/json"
"fmt"
"github.com/liuhongdi/gintest01/router"
"github.com/stretchr/testify/assert"
"io/ioutil"
"github.com/liuhongdi/gintest01/pkg/unittest"
"net/http/httptest"
"testing"
)
type ResultCont struct {
Code int `json:"code"` // 自增
Msg string `json:"msg"` //
Data interface{} `json:"data"`
}
//测试得到用户信息
func TestGet(t *testing.T) {
globalrouter = router.Router()
var w *httptest.ResponseRecorder
urlIndex := "/user/get?uid=3"
w = unittest.Get(urlIndex, globalrouter)
assert.Equal(t,200, w.Code)
//result := w.Body
body,_ := ioutil.ReadAll(w.Body)
m:=&ResultCont{}
err := json.Unmarshal(body, &m)
if err != nil {
fmt.Println("Umarshal failed:", err)
return
}
assert.Equal(t,0, m.Code)
//assert.Equal(t,"abc", m.Msg)
}
//测试post登录
func TestLogin(t *testing.T) {
globalrouter = router.Router()
var w *httptest.ResponseRecorder
//test post
param := make(map[string]string)
param["username"] = "username"
param["password"] = "password"
urlLogin := "/user/post"
w = unittest.PostForm(urlLogin, param, globalrouter)
assert.Equal(t,200, w.Code)
body,_ := ioutil.ReadAll(w.Body)
m:=&ResultCont{}
err := json.Unmarshal(body, &m)
if err != nil {
fmt.Println("Umarshal failed:", err)
return
}
if (m.Code != 0){
t.Fatalf("fail,code:%d,msg:%s",m.Code,m.Msg)
}
}
四,测试效果
root@ku:/data/go/gintest01# go test -v ./...
=== RUN TestGet
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /user/get --> github.com/liuhongdi/gintest01/controller.(*UserController).GetUser-fm (4 handlers)
[GIN-debug] POST /user/post --> github.com/liuhongdi/gintest01/controller.(*UserController).PostUser-fm (4 handlers)
[GIN] 2021/01/26 - 18:05:45 | 200 | 63.747µs | 192.0.2.1 | GET "/user/get?uid=3"
--- PASS: TestGet (0.00s)
=== RUN TestLogin
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /user/get --> github.com/liuhongdi/gintest01/controller.(*UserController).GetUser-fm (4 handlers)
[GIN-debug] POST /user/post --> github.com/liuhongdi/gintest01/controller.(*UserController).PostUser-fm (4 handlers)
[GIN] 2021/01/26 - 18:05:45 | 200 | 118.343µs | 192.0.2.1 | POST "/user/post?username=username&password=password"
--- PASS: TestLogin (0.00s)
PASS
ok github.com/liuhongdi/gintest01 0.011s
? github.com/liuhongdi/gintest01/controller [no test files]
? github.com/liuhongdi/gintest01/global [no test files]
? github.com/liuhongdi/gintest01/pkg/unittest [no test files]
? github.com/liuhongdi/gintest01/router [no test files]
五,查看库的版本
module github.com/liuhongdi/gintest01
go 1.15
require (
github.com/gin-gonic/gin v1.6.3
github.com/stretchr/testify v1.6.1
)