『GCTT 出品』测试 Go 语言 Web 应用

2018-03-14 10:51:49 译者zhuCheer Go语言中文网

已发布:https://studygolang.com/articles/12153

我利用闲暇的时间尝试着用 Go 来写一个网站小应用。在 Go 标准库中有一些非常棒的包可以在 Web 应用开发中使用并且我非常喜欢使用它们。实际上,在 Go 官方的 wiki 中有一个编写 Web 应用的小教程。但是,却没有提及如何用标准库去测试 Web 应用,而且也没有搜索到比较好的一个方案。

本着试一试的心态在我自己的项目中做了一些尝试,我发现了测试 Go 语言中 Web 应用的关键,就是通过使用高级的函数来实现依赖注入。

依赖注入

通过依赖注入我们希望能达到可以支持我们所有的功能要求这样一个目标。

然而,刚开始不太清楚怎么做。大多数的教程都会像这样写一个 Web 应用:

package mainimport (    "fmt"
    "net/http")func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

来源: 编写一个 Web 应用 - Golang Wiki

并不是说这是一个不好的处理方法,但是我们想要知道当添加一个数据库时发生了什么呢?或者一个外部的包要操作我们的 sessions ,像 Gorilla Session 那样么?通常,人们会实例化一个数据库来管理或者操作一个全局的 session 并且将有效期设置成一天。但这样当你试图测试时将会给你带来麻烦。最好是编写一个函数来为你创建相关的操作。

package main

import (
   "fmt"    "net/http"    "github.com/markberger/database"
)

type
AppDatabase interface {    GetBacon() string
}

func homeHandler(db AppDatabase) http.Handler {
   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {        fmt.Fprintf(w, "Hi there, I love %s!", db.GetBacon())    }) }

func
main() {    db := database.NewDatabase()    http.HandleFunc("/", homeHandler(db))    http.ListenAndServe(":8080", nil) }

这样是非常好的,因为这样我们就不再需要全局变量了。而且这样能非常轻松的 mocking (译者注:用一个虚拟的对象来创建以便测试的测试方法)。我们只需要实现 GetBacon 这个方法,就能简单地创建一个数据库 mock 来满足接口的数据需求。

测试

现在我们需要一个项目来开始编写我们的测试。关键是测试 http.Handler,用的是 net/http/httptest 包中的 httptest.ResponseRecorder 里面的方法。

package main

import(
   "net/http"    "net/http/httptest"    "testing"
)

type
MockDd struct {} function (db MockDb) GetBacon() {
   return "bacon"
} function TestHome(t *testing.T) {    mockDb := MockDb{}    homeHandle := homeHandler(mockDb)    req, _ := http.NewRequest("GET", "", nil)    w := httptest.NewRecorder()    homeHandle.ServeHTTP(w, req)
   if w.Code != http.StatusOK {        t.Errorf("Home page didn't return %v", http.StatusOK)    } }

当然,这些代码能很好的复用,尤其是进行 POST 请求测试时。因此,我一直在使用这些方法来进行测试操作。

package main

import
(
   "net/http"    "net/http/httptest"    "net/url"    "testing"
)
   
type HandleTester func(    method string,    params url.Values, ) *httptest.ResponseRecorder

// Given the current test runner and an http.Handler, generate a
// HandleTester which will test its given input against the
// handler.

func GenerateHandleTester(    t *testing.T,    handleFunc http.Handler, ) HandleTester {
   // Given a method type ("GET", "POST", etc) and    // parameters, serve the response against the handler and    // return the ResponseRecorder.    return func(        method string,        params url.Values,    ) *httptest.ResponseRecorder {        req, err := http.NewRequest(            method,            "",            strings.NewReader(params.Encode()),        )
       if err != nil {            t.Errorf("%v", err)        }        req.Header.Set(
           "Content-Type",
           "application/x-www-form-urlencoded; param=value",        )        w := httptest.NewRecorder()        handleFunc.ServeHTTP(w, req)
       return w    } } function TestHome(t *testing.T) {    mockDb := MockDb{}    homeHandle := homeHandler(mockDb)    test := GenerateHandleTester(t, homeHandle)    w := test("GET", url.Values{})
   if w.Code != http.StatusOK {        t.Errorf("Home page didn't return %v", http.StatusOK)    } }

更多相关的详细用法,可以点击查看我的小项目。

如果你有更好的方法来利用标准库进行 web 应用测试,你可以随时进行留言或给我发 email。


via: http://markjberger.com/testing-web-apps-in-golang/

作者:Mark J. Berger
译者:zhuCheer
校对:polaris1119

本文由 GCTT 原创编译,Go 中文网 荣誉推出


喜欢本文的朋友们,欢迎长按下图关注订阅号Go语言中文网,收看更多精彩内容