本篇文章使用 testify 測試框架,對使用 gin 後端框架開發的應用程式進行測試

  • Assert Path
  • Assert Query String
  • Assert Body Contains
  • Assert Status OK
  • Follow Redirects
  • Get
  • Post
  • 測試 Html
  • 測試 Redirects
  • 測試 Session

Assert Path

斷言 Request 的路徑

1
2
3
func assertPath(t *testing.T, req *http.Request, expected string) {
assert.Equal(t, expected, req.URL.Path)
}

Assert Query String

斷言 Request 的查詢參數內容

1
2
3
func assertQuery(t *testing.T, req *http.Request, expected url.Values) {
assert.Equal(t, expected.Encode(), req.URL.RawQuery)
}

Assert Body Contains

斷言 Response 內容包含字串

1
2
3
func assertContains(t *testing.T, w *httptest.ResponseRecorder, expected string) {
assert.Contains(t, w.Body.String(), expected)
}

Assert Status OK

斷言 Response 的 http code 是 200

1
2
3
func assertStatusOK(t *testing.T, w *httptest.ResponseRecorder) {
assert.Equal(t, http.StatusOK, w.Code)
}

Follow Redirects

持續追蹤直到 Response 沒有 Location,回傳最後一個 Response 和 Request

1
2
3
4
5
6
7
8
9
10
11
func followRedirects(router *gin.Engine, w *httptest.ResponseRecorder, req *http.Request) (*httptest.ResponseRecorder, *http.Request) {
location := w.Header().Get("Location")
if location == "" {
return w, req
}

req.URL, _ = url.Parse(location)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
return followRedirects(router, w, req)
}

Get

建立一個到指定路徑的 Get 請求,可帶入 Query string

1
2
3
4
5
6
7
8
9
10
11
func get(router *gin.Engine, path string, data url.Values, follow bool) (*httptest.ResponseRecorder, *http.Request) {
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, path, nil)
req.URL.RawQuery = data.Encode()
router.ServeHTTP(w, req)

if follow {
return followRedirects(router, w, req)
}
return w, req
}

Post

建立一個到指定路徑的 Post 請求,可帶入 form data,繼續 follow 時採用 Get 執行

1
2
3
4
5
6
7
8
9
10
11
12
13
func post(router *gin.Engine, path string, data url.Values, follow bool) (*httptest.ResponseRecorder, *http.Request) {
w := httptest.NewRecorder()
body := strings.NewReader(data.Encode())
req, _ := http.NewRequest(http.MethodPost, path, body)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
router.ServeHTTP(w, req)

if follow {
req.Method = http.MethodGet
return followRedirects(router, w, req)
}
return w, req
}

測試 Html

測試 Html 包含

  1. 路徑
  2. 特定內容
  3. 狀態
1
2
3
4
5
6
7
func TestHome(t *testing.T) {
router := setupRouter()
w, req := get(router, "/", nil, true)
assertPath(t, req, "/")
assertContains(t, w, "歡迎光臨,請輸入帳號密碼")
assertStatusOK(t, w)
}

測試 Redirects

測試 Redirects 包含

  1. 路徑
  2. 特定內容
  3. 狀態
1
2
3
4
5
6
7
func TestRedirectsMemberToHomeIfNotLogin(t *testing.T) {
router := setupRouter()
w, req := get(router, "/member", nil, true)
assertPath(t, req, "/")
assertContains(t, w, "歡迎光臨,請輸入帳號密碼")
assertStatusOK(t, w)
}

測試 Session

加入一個路由 api 設定 session,重新導入預期的第一個頁面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func TestRedirectsHomeToMemberIfLogin(t *testing.T) {
router := setupRouter()
router.GET("/mockapi", func(ctx *gin.Context) {
session := sessions.Default(ctx) // gin-contrib/sessions
session.Set("isLogin", true)
session.Save()
Redirect(ctx, http.StatusFound, "/")
})

w, req := get(router, "/mockapi", nil, true)
assertPath(t, req, "/member")
assertContains(t, w, "歡迎光臨,這是會員頁")
assertStatusOK(t, w)
}