本文首发于我的博客
简述
在日常后端业务开发中,我们经常会写一些 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/*
复制代码