Gin是Go语言的一个高性能web框架,而单元测试是开发的一个非常重要的环节,可以在应用发布之前发现问题。
今天我们就来看看Gin的处理程序单元测试如何写。首先我们用gin创建一个”hello word“ HTTP服务器。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func IndexHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "hello world",
})
}
func SetupServer() *gin.Engine {
r := gin.Default()
r.GET("/", IndexHandler)
return r
}
func main() {
SetupServer().Run()
}
运行上面的应用程序,然后对http://localhost:8080发起一个GET请求,将收到“hello world”字符串。
$ curl localhost:8080
{"message":"hello world"}”
上面的代码使用SetupServer()函数返回*gin.Engine实例,便于后续使用httpserver模拟服务端,如下所示:
package main
func TestIndexHandler(t *testing.T) {
mockUserResp := `{"message":"hello world"}`
ts := httptest.NewServer(SetupServer())
defer ts.Close()
resp, err := http.Get(fmt.Sprintf("%s/", ts.URL))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {”
“t.Fatalf("Expected status code 200, got %v", resp.StatusCode)
}
responseData, _ := ioutil.ReadAll(resp.Body)
if string(responseData) != mockUserResp {
t.Fatalf("Expected hello world message, got %v", responseData)
}
}
运行测试用例结果如下:
/usr/local/go/bin/go tool test2json -t /private/var/folders/x6/8wtj7zfd7r59wpk5fmjln2p40000gn/T/___TestIndexHandler_in_awesomeProject -test.v -test.paniconexit0 -test.run ^\QTestIndexHandler\E$
=== RUN TestIndexHandler
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET / --> awesomeProject.IndexHandler (3 handlers)
[GIN] 2022/02/03 - 23:44:02 | 200 | 72.875µs | 127.0.0.1 | GET "/"
--- PASS: TestIndexHandler (0.00s)
PASS
虽然您可以使用testing包编写完整的测试,但您也可以安装第三方包testify来使用高级断言。要做到这一点,请遵循以下步骤:
1、下载testify第三方包:
Go get github.com/stretchr/testify
2、更新测试用例TestIndexHandler,使用testify包的assert属性:
func TestIndexHandler(t *testing.T) {
mockUserResp := `{"message":"hello world"}`
ts := httptest.NewServer(SetupServer())
defer ts.Close()
resp, err := http.Get(fmt.Sprintf("%s/", ts.URL))
defer resp.Body.Close()
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
responseData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, mockUserResp, string(responseData))
}
go test执行结果和前面一样的。
为特定Handler写测试用例
假设你的代码里面有自己API路由及其处理程序,如下所示:
func main() {
router := gin.Default()
router.POST("/recipes", NewRecipeHandler)
router.GET("/recipes", ListRecipesHandler)
router.PUT("/recipes/:id", UpdateRecipeHandler)
router.DELETE("/recipes/:id", DeleteRecipeHandler)
router.GET("/recipes/:id", GetRecipeHandler)
router.Run()
}
GET请求处理程序单元测试
下面以ListRecipesHandler处理程序为例写单元测试:
//为测试使用创建 *gin.Engine实例
func SetupRouter() *gin.Engine {
router := gin.Default()
return router
}
func TestListRecipesHandler(t *testing.T) {
r := SetupRouter()
//将项目中的API注册到测试使用的router
r.GET("/recipes", ListRecipesHandler)
//向注册的路有发起请求
req, _ := http.NewRequest("GET", "/recipes", nil)
w := httptest.NewRecorder()
//模拟http服务处理请求
r.ServeHTTP(w, req)
var recipes []Recipe
json.Unmarshal([]byte(w.Body.String()), &recipes)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, 492, len(recipes))
}
POST请求处理程序单元测试
func TestNewRecipeHandler(t *testing.T) {
r := SetupRouter()
r.POST("/recipes", NewRecipeHandler)
//创建post请求体
recipe := Recipe{
Name: "New York Pizza",
}
//序列化请求体
jsonValue, _ := json.Marshal(recipe)
//发起post请求
req, _ := http.NewRequest("POST", "/recipes", bytes.NewBuffer(jsonValue))
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}