场景
假设我们有一个HTTP
服务,里面定义了两个接口,当我们想要为这两个API编写单测,我们该如何做呢?
很多小伙伴首先想到的是直接使用net/http
包中的http.Get(url)
去请求我们的API地址,再断言响应结果。
上面的办法当然也没问题,但是想成功跑过单测,我们还必须先启动我们的HTTP
服务。
正确的单元测试
在Golang
中,官方为我们提供了更方便的HTTP
测试框架net/http/httptest
。
使用httptest
后,我们可以直接模拟发送HTTP
请求,而且不需要再单独去启动HTTP
服务。
基于Gin的单元测试之httptest
httptest
的使用相当简单,相信你看到示例代码后秒懂~
话不多说,下面直接上代码。
在main.go
中我们定义了两个API
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := SetupRouter()
router.Run(":8881")
}
// 在gin中注册路由,并写了两个接口样例
func SetupRouter() *gin.Engine {
router := gin.Default()
// GET接口
router.GET("/hello", Hello)
// POST接口
router.POST("/auth/mail", AuthMail)
return router
}
func Hello(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "",
"data": map[string]interface{}{
"id": ctx.Query("id"),
},
})
}
func AuthMail(ctx *gin.Context) {
type Params struct {
Email string `json:"email"`
Password string `json:"password"`
}
var params Params
if err := ctx.ShouldBindJSON(¶ms); err != nil {
ctx.AbortWithStatusJSON(
http.StatusInternalServerError,
gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "",
"data": map[string]interface{}{
"accessToken": "Bearer xxx",
"email": params.Email,
},
})
}
创建api_test.go
编写我们的单元测试代码
其中newRequest()
是我封装的一个通用请求方法
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 封装的通用请求方法
func newRequest(method, path string, header map[string]string, body io.Reader) *httptest.ResponseRecorder {
// 直接复用定义好 gin 路由实例
router := SetupRouter()
req, _ := http.NewRequest(method, path, body)
for k, v := range header {
req.Header.Set(k, v)
}
// 初始化响应
w := httptest.NewRecorder()
// 调用相应handler接口
router.ServeHTTP(w, req)
defer w.Result().Body.Close()
return w
}
// 模拟GET请求
func TestHelloWorld(t *testing.T) {
w := newRequest(http.MethodGet, "/hello?id=1000", map[string]string{
"Content-Type": "application/json",
"Authorization": "",
}, nil)
assert.Equal(t, http.StatusOK, w.Code)
var response Response
err := json.Unmarshal([]byte(w.Body.String()), &response)
assert.Nil(t, err)
assert.Equal(t, response.Code, 0)
if data, ok := response.Data.(map[string]string); ok {
assert.Equal(t, 1000, data["id"])
}
t.Log("响应内容", response.Data)
}
func TestAuthMail(t *testing.T) {
params, err := json.Marshal(map[string]interface{}{
"email": "5303221@gmail.com",
"password": "123456",
})
assert.Nil(t, err)
w := newRequest(
http.MethodPost,
"/auth/mail",
map[string]string{
"Content-Type": "application/json",
"Authorization": "",
},
bytes.NewBuffer(params))
assert.Equal(t, http.StatusOK, w.Code)
var response Response
err = json.Unmarshal([]byte(w.Body.String()), &response)
assert.Nil(t, err)
assert.Equal(t, response.Code, 0)
if data, ok := response.Data.(map[string]string); ok {
assert.Equal(t, "5303221@gmail.com", data["email"])
}
t.Log("响应内容", response.Data)
}
执行go test -v .
运行我们的单元测试,就可以看到我们最终的测试结果啦
=== RUN TestHelloWorld
[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 /hello --> gin-demo.Hello (3 handlers)
[GIN-debug] POST /auth/mail --> gin-demo.AuthMail (3 handlers)
[GIN] 2021/01/04 - 17:35:45 | 200 | 0s | | GET "/hello?id=1000"
api_test.go:52: 响应内容 map[id:1000]
--- PASS: TestHelloWorld (0.01s)
=== RUN TestAuthMail
[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 /hello --> gin-demo.Hello (3 handlers)
[GIN-debug] POST /auth/mail --> gin-demo.AuthMail (3 handlers)
[GIN] 2021/01/04 - 17:35:45 | 200 | 0s | | POST "/auth/mail"
api_test.go:81: 响应内容 map[accessToken:Bearer xxx email:5303221@gmail.com]
--- PASS: TestAuthMail (0.00s)
PASS
ok gin-demo 1.923s
评论(0)