看完 justforfunc #16: unit testing HTTP servers 影片後,了解原來單元測試 HTTP Server 並不會太難,因為官方已經有提供 httptest 工具,這工具讓單元測試 HPPT Server 變得非常簡單。

我平常都是使用 Gin 開發網站,而單元測試 HTTP Server 的概念都一樣,都是使用 httptest ,所以今天就來筆記一下如何寫 Gin 的 HTTP Server 單元測試。

範例

下方是這篇要測試的範例,範例非常簡單,我建立一個 /hello/:name 的 API,回傳 Hello, :name 的訊息。

這邊要注意的是,要把建立 gin.Engine 與設定路由抽一個方法出來,因為等等寫測試的時候,會需要取得 gin.Engine 物件。

main.go
  • go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)

func main() {
r := setupRouter()
r.Run()
}

func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/hello/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, fmt.Sprintf("Hello, %s", name))
})
return r
}

測試程式碼

以單元測試來說,實際上我不會透過網路去執行 API 呼叫,而是模擬 API 呼叫後,取得該路由的回應,而要取得回應的話,會需要 ResponseWriter 來接回應的資料,httptest 就有提供一個有實作 ResponseWriterResponseRecorder 物件 ,且 ResponseRecorderStatus Code, Body, Header 的欄位,能夠讓我們驗證回應是否正確。

下面的程式碼示範如何用 ResponseRecorder 寫單元測試

  1. 取得剛剛抽出來,設定好路由的 gin.Engine 物件
  2. 使用 httptest 取得 ResponseRecorder 物件
  3. 使用 http 建立一個 Request
  4. 使用 gin 提供的 ServeHTTP 模擬 API 呼叫
  5. 這時候 API 的回應就會被寫入 ResponseRecorder 物件了,這樣我們就可以驗證回應正不正確了。
main_test.go
  • go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)

func Test_setupRouter(t *testing.T) {
router := setupRouter()

w := httptest.NewRecorder() // 取得 ResponseRecorder 物件
req, _ := http.NewRequest("GET", "/hello/miles", nil)
router.ServeHTTP(w, req)

assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "miles")
}

延伸閱讀

[justforfunc #16: unit testing HTTP servers]
[gin-gonic]
[testify/assert]
[httptest/example_test.go]