go test 常见套路(2)

2019-09-18 08:55:15 +08:00
 guonaihong

上回聊到,golang 里面写一个简单的测试函数,和 benchmark 函数,如何检测测试代码覆盖度。 这回聊下如何编写 http server 测试代码。如果写过 API 服务的童鞋都知道。 测试服务端代码要先起个服务,再运行客户端代码。有没有办法和 go test 方法结合再一起,可以很快乐的编写服务端测试代码呢?

标准库服务端测试代码

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"
)

func main() {
    // 启动一个测试服务
    ts := httptest.NewServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    })) 
    defer ts.Close()

    // 关键一步, 客户端完全使用标准套路,注意:url 使用 ts.URL 就行
    res, err := http.Get(ts.URL)
    if err != nil {
        log.Fatal(err)
    }   
    greeting, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        log.Fatal(err)
    }   

    fmt.Printf("%s", greeting)
}

gin 框架测试代码

func TestShouldBindXML(t *testing.T) {
    var d, d2 data
    router := func() *gin.Engine {
        router := gin.Default()

        router.POST("/test.xml", func(c *gin.Context) {
            var d3 data
            err := c.ShouldBindXML(&d3)
            assert.NoError(t, err)
            c.XML(200, d3)
        })
        return router
    }()

    // 这里是 gin 和 http 标准库的区别,需要拿到 gin.Engine 的 ServeHTTP 函数
    ts := httptest.NewServer( http.HandlerFunc(router.ServeHTTP))
    defer ts.Close()

    g := New(nil)

    d.Id = 3
    d.Data = "test data"

    code := 200

    err := g.POST(ts.URL + "/test.xml").SetXML(&d).BindXML(&d2).Code(&code).Do()

    assert.NoError(t, err)
    assert.Equal(t, code, 200)
    assert.Equal(t, d, d2)
}

基于 func(w http.ResponseWriter, r *http.Request) 编写测试代码

这个方法应用场景在编写一些库的时候会用到,只想模拟一个服务端跑 http handle 函数,里面可以设置 http header 等返回属性。然后客户端直接获得*http.Response 对象操作。可以理解为这是一个很薄薄的服务端测试写法

func TestHeaderDecode(t *testing.T) {
    h := headerDecode{}

    type tHeader struct {
        Limit      int           `header:"limit"`
        F64        float64       `header:"f64"`
        F32        float32       `header:"f32"`
        CreateTime time.Time     `header:"createTime" time_format:"unixNano"`
        UnixTime   time.Time     `header:"unixTime" time_format:"unix"`
        Duration   time.Duration `header:"duration"`
    }

    var theader tHeader

    // 和正常的服务端回调函数一样
    okFunc := func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("limit", "1000")
        w.Header().Add("f64", "64")
        w.Header().Add("f32", "32.1")
        w.Header().Add("createTime", "1562400033000000123")
        w.Header().Add("unixTime", "1562400033")
        w.Header().Add("duration", "1h1s")
    }

    // 主要构造出 http.Request 对象
    req := httptest.NewRequest("GET", "http://example.com/foo", nil)
    // 构造出 http.ResponseWriter 对象
    w := httptest.NewRecorder()
    //执行回调函数
    okFunc(w, req)
    //构造出*http.Response 对象
    resp := w.Result()

    // todo array slice
    assert.NoError(t, h.Decode(resp, &theader))

}

我的 github 地址

https://github.com/guonaihong/gout

2431 次点击
所在节点    Go 编程语言
4 条回复
richzhu
2019-09-18 11:42:47 +08:00
支持,谢谢分享
edward8628
2019-09-18 11:55:05 +08:00
感谢分享
guonaihong
2019-09-18 12:46:34 +08:00
@richzhu 喜欢就好,后面我会把内容整理好,做成 gitbook,方便查看。
guonaihong
2019-09-18 12:47:53 +08:00
@edward8628 喜欢就好。后面还会分享 go channel 相关技巧。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/601697

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX