首页
Preview

使用httptest为REST API编写单测

场景

假设我们有一个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(&params); 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


版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
sunnya
暂无描述

评论(0)

添加评论