本文首发于我的博客
简述
在日常后端业务开发中,我们经常会写一些 api,然后用 postman
测试下是否可用,可能就直接丢到线上去了。 然鹅这样做非常不严谨, 大部分情况下还是需要对 api 进行测试,以保证可用性。 我在项目中用到的是 httpexpect, 跟 nodejs 中的 mocha 有一些类似。在这里就不对基本的单元测试做介绍了,大家翻翻 golang 入门指南之类的文档就能看到。
使用
httpexpect 是一个端对端 api 测试工具
End-to-end HTTP and REST API testing for Go.
安装
go get -u -v github.com/gavv/httpexpect
一个小例子
package example
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gavv/httpexpect"
)
func TestFruits(t *testing.T) {
// 创建 http.Handler
handler := FruitsHandler()
// 运行 server
server := httptest.NewServer(handler)
defer server.Close()
// 创建 httpexpect 实例
e := httpexpect.New(t, server.URL)
// 测试api是否工作
e.GET("/test").
Expect().
Status(http.StatusOK).JSON().Array().Empty()
}
支持 json 数据校验
orange := map[string]interface{}{
"weight": 100,
}
// GET 创建一个橘子
e.PUT("/fruits/orange").WithJSON(orange).
Expect().
Status(http.StatusNoContent).NoContent()
// GET 然后获取, 并校验数据中是否含有 weight: 100
e.GET("/fruits/orange").
Expect().
Status(http.StatusOK).
JSON().Object().ContainsKey("weight").ValueEqual("weight", 100)
apple := map[string]interface{}{
"colors": []interface{}{"green", "red"},
"weight": 200,
}
// 创建一个苹果
e.PUT("/fruits/apple").WithJSON(apple).
Expect().
Status(http.StatusNoContent).NoContent()
// 获取这个苹果
obj := e.GET("/fruits/apple").
Expect().
Status(http.StatusOK).JSON().Object()
obj.Keys().ContainsOnly("colors", "weight")
// 对 返回数据逐一测试
obj.Value("colors").Array().Elements("green", "red")
obj.Value("colors").Array().Element(0).String().Equal("green")
obj.Value("colors").Array().Element(1).String().Equal("red")
obj.Value("colors").Array().First().String().Equal("green")
obj.Value("colors").Array().Last().String().Equal("red")
链式调用函数用起来很顺手,其实内置函数还有很多, Object 数据类型有如下函数等等,满足各种测试需要。
ContainsKey
ContainsMap
Empty
Equal
Keys
NotContainsKey
当然也支持其他场景的测试, 比如
JSON Schema and JSON Path
JSON 模式Forms
表单URL construction
url 构造HTTP Headers
headerCookies
cookieRegular expressions
正则Subdomains and per-request URL
子 urlReusable builders
可重复使用Custom config
自定义Session support
session会话支持Use HTTP handler directly
重定向
实际应用
下面是一个依赖 gin 框架 api 项目使用 httpexpect 的例子。
- app.go
package main
import (
"./engine"
)
func main() {
engine.GetMainEngine().Run(":4000")
}
- engine/engine.go, 这里之所以多一个 engine.go, 是因为我们要把 *gin.Engine 返回给 httpexpect, 创建 server,参考 node.js 项目 api 测试。
package engine
import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
func GetMainEngine() *gin.Engine {
r := gin.New()
// db, store := database.Connect()
// logdb := database.ConnectLog()
// r.Use(sessions.Sessions("xxx", store))
// r.Use(corsMiddleware())
// r.Use(gin.Logger())
// r.Use(gin.Recovery())
// r.Use(requestLogger())
// 一堆自定义的 handler
routers.Init(r)
return r
}
- articles_test.go
package test
import (
"net/http"
"testing"
)
var eng *httpexpect.Expect
func GetEngine(t *testing.T) *httpexpect.Expect {
gin.SetMode(gin.TestMode)
if eng == nil {
server := httptest.NewServer(engine.GetMainEngine())
eng = httpexpect.New(t, server.URL)
}
return eng
}
func TestArticles(t *testing.T) {
e := GetEngine(t)
e.GET("/api/v1/articles").
Expect().
Status(http.StatusOK).
JSON().Object().ContainsKey("data").Keys().Length().Ge(0)
}
然后执行
go test -v -cover ...
执行结果类似:
使用这个包,我们可以对 restful 的每个 api 都进行测试 ????, 更大程度地提升了代码质量。以下是我的 .travis.yml 配置。 不足之处,请批评指正!
language: go
services: mongodb
go:
- 1.9.2
- master
install: true
matrix:
allow_failures:
- go: master
fast_finish: true
notifications:
email: false
script:
- echo "script"
- go get -u -v github.com/gavv/httpexpect
- 其他自定义
- echo "add config file"
- cp config/config.example.yaml config/config.yaml
- echo "test"
- export GIN_MODE=test
- go test -v -cover test/*