Go单测从零到溜系列——1.网络测试
source link: https://www.liwenzhou.com/posts/Go/golang-unit-test-1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
这是Go语言单元测试从零到溜系列教程的第1篇,介绍了如何使用httptest和gock工具进行网络测试。
Go语言单元测试从零到溜系列共7篇,本文是第1篇,介绍了如何使用httptest和gock工具进行网络测试。
httptest
在Web开发场景下的单元测试,如果涉及到HTTP请求话使用Go标准库 net/http/httptest
进行测试更为高效。
以常见的gin框架为例,我们现在搭建了一个server端,提供了一个helloHandler
函数:
// gin.go
// Param 请求参数
type Param struct {
Name string `json:"name"`
}
// helloHandler /hello请求处理函数
func helloHandler(c *gin.Context) {
var p Param
if err := c.ShouldBindJSON(&p); err != nil {
c.JSON(http.StatusOK, gin.H{
"msg": "we need a name",
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": fmt.Sprintf("hello %s", p.Name),
})
}
// SetupRouter 路由
func SetupRouter() *gin.Engine {
router := gin.Default()
router.POST("/hello", helloHandler)
return router
}
这个时候要为helloHandler
编写单元测试,就可以使用httptest
创建请求和响应。
// gin_test.go
func Test_helloHandler(t *testing.T) {
tt := []struct {
name string
param string
expect string
}{
{"base case", `{"name": "liwenzhou"}`, "hello liwenzhou"},
{"bad case", "", "we need a name"},
}
r := SetupRouter()
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
// 构建请求
req := httptest.NewRequest(
"POST",
"/hello",
strings.NewReader(tc.param),
)
// 获取响应
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// 解析并检验响应内容
var resp map[string]string
err := json.Unmarshal([]byte(w.Body.String()), &resp)
assert.Nil(t, err)
assert.Equal(t, tc.expect, resp["msg"])
})
}
}
如果是在代码中请求外部API的场景,比如我们项目中通过API调用其他服务。
// api.go
// ReqParam API请求参数
type ReqParam struct {
X int `json:"x"`
}
// Result API返回结果
type Result struct {
Value int `json:"value"`
}
func GetResultByAPI(x, y int) int {
p := &ReqParam{X: x}
b, _ := json.Marshal(p)
// 调用其他服务的API
resp, err := http.Post(
"http://your-api.com/post",
"application/json",
bytes.NewBuffer(b),
)
if err != nil {
return -1
}
body, _ := ioutil.ReadAll(resp.Body)
var ret Result
if err := json.Unmarshal(body, &ret); err != nil {
return -1
}
// 对API返回的数据做一些逻辑处理
return ret.Value + y
}
在对这些代码编写单元测试的时候,如果不想在测试过程中真正去发送请求或者依赖的接口还没有开发完成时,我们可以对依赖的API进行mock。
这里推荐使用gock这个库。
go get -u gopkg.in/h2non/gock.v1
// api_test.go
func TestGetResultByAPI(t *testing.T) {
defer gock.Off() // 测试执行后刷新挂起的mock
// mock 请求外部api时传参x=1返回100
gock.New("http://your-api.com").
Post("/post").
MatchType("json").
JSON(map[string]int{"x": 1}).
Reply(200).
JSON(map[string]int{"value": 100})
res := GetResultByAPI(1, 1)
assert.Equal(t, res, 101)
// mock 请求外部api时传参x=2返回200
gock.New("http://your-api.com").
Post("/post").
MatchType("json").
JSON(map[string]int{"x": 2}).
Reply(200).
JSON(map[string]int{"value": 200})
res = GetResultByAPI(2, 2)
assert.Equal(t, res, 202)
assert.True(t, gock.IsDone()) // 确定mock被触发
}
在Web开发场景下为代码编写单元测试时如何处理外部依赖是最常见的问题,本文介绍了如何使用httptest和gock工具mock相关依赖。 在下一篇中,我们将更进一步,详细介绍针对依赖MySQL和Redis的场景如何编写单元测试。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK