這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文首發於我的部落格
簡述
在日常後端業務開發中,我們經常會寫一些 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 exampleimport ( "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: 100e.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 資料類型有如下函數等等,滿足各種測試需要。
ContainsKeyContainsMapEmptyEqualKeysNotContainsKey
當然也支援其他情境的測試, 比如
JSON Schema and JSON Path
JSON 模式
Forms
表單
URL construction
url 構造
HTTP Headers
header
Cookies
cookie
Regular expressions
正則
Subdomains and per-request URL
子 url
Reusable builders
可重複使用
Custom config
自訂
Session support
session會話支援
Use HTTP handler directly
重新導向
實際應用
下面是一個依賴 gin 架構 api 項目使用 httpexpect 的例子。
- app.go
package mainimport ( "./engine")func main() { engine.GetMainEngine().Run(":4000")}
- engine/engine.go, 這裡之所以多一個 engine.go, 是因為我們要把 *gin.Engine 返回給 httpexpect, 建立 server,參考 node.js 項目 api 測試。
package engineimport ( "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 testimport ( "net/http" "testing")var eng *httpexpect.Expectfunc 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: goservices: mongodbgo: - 1.9.2 - masterinstall: truematrix: allow_failures: - go: master fast_finish: truenotifications: email: falsescript: - 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/*