go test常见套路(2)

原创
2019/09/20 09:14
阅读数 150

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

标准库服务端测试代码

先使用httptest.NewServer声明一个mock服务ts,再使用ts.URL给客户端用就行

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 框架测试代码

使用gin和标准库相比多了一步,需要把gin.Engine。转成 http.Handle func(w http.ResponseWriter, r *http.Request)类型 其实这个类型就藏在gin.Engine.ServeHTTP 成员里面

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

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部