本文首發於我的部落格
簡述
在日常後端業務開發中,我們經常會寫一些 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/*
複製程式碼