测试 Go 语言 Web 应用

zhuCheer · 2018-01-11 14:09:20 · 1753 次点击 · 预计阅读时间 4 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-01-11 14:09:20 的文章,其中的信息可能已经有所发展或是发生改变。

image

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

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

依赖注入

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

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

package main

import (
    "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大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1753 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传