Blog.3 使用httptest模拟接口测试

Test中在模拟接口测试,首先咱们先实现一个最基础的Test例子:json

模拟一个ping/pong的最基本请求,咱们先写一个返回pongHTTP handlerapp

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

func Pong(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "text/plain")
    io.WriteString(w, "pong")
}

而后写测试用例:框架

func TestRequest(t *testing.T) {
    req, err := http.NewRequest("GET", "ping", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(Pong)

    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf("status code %v", rr.Code)
    }

    if rr.Body.String() != "pong" {
        t.Errorf("returned %s", rr.Body.String())
    }
}

程序日志输出Pass,这个小demo正常运行了。而后咱们在这个基础上,咱们给请求增长一个超时时间、以及携带header头等信息测试

咱们将请求的header头返回,处理的hander以下:ui

func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")

    output, _ := json.Marshal(r.Header)
    io.WriteString(w, string(output))
}

而后是咱们的测试用例, 并且也更贴近咱们的真实开发:日志

func TestRequest(t *testing.T) {
    req, _ := http.NewRequest("GET", "/user/info", nil)

    // 设置header头
    req.Header.Set("uid", "10086")

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(GetUsersHandler)

    // 给请求设置1s的超时
    ctx := req.Context()
    ctx, _ = context.WithTimeout(ctx, time.Second)

    req = req.WithContext(ctx)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf("status code %v", rr.Code)
    }
    t.Log(rr.Body.String())
}

而后咱们追加一个middleware来让代码更加真实,middleware的做用就是在context中设置auth的结果。code

// 中间件,在context中设置一个auth
func MiddlewareHandler(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        ctx := context.WithValue(r.Context(), "auth", "middle verify")
        h.ServeHTTP(w, r.WithContext(ctx))
    }
    return http.HandlerFunc(fn)
}

连带的对测试的方法作一下简单的调整:orm

func TestRequest(t *testing.T) {
    req, _ := http.NewRequest("GET", "/user/info", nil)

    // 设置header头
    req.Header.Set("uid", "10086")

    // 设置middleware
    rr := httptest.NewRecorder()
    handler := MiddlewareHandler(http.HandlerFunc(GetUsersHandler))

    // 给请求设置1s的超时
    ctx, _ := context.WithTimeout(req.Context(), time.Second)

    req = req.WithContext(ctx)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf("status code %v", rr.Code)
    }
    t.Log(rr.Body.String())
}

最后是将经过middleware传递的context值打印输出:router

func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")

    output, _ := json.Marshal(r.Context().Value("auth"))
    io.WriteString(w, string(output))
}

继续按照上面的方法,咱们使用gin框架在作一次尝试。接口/user/info经过Bind的方式来获取参数。server

咱们在方法体内声明告终构体,同时将类型定义为interface。当收到客户端传递的参数时,解析到变量r上,最后json格式打印输出

func TestRequest(t *testing.T) {
    req, _ := http.NewRequest("GET", "/user/info?type=1", nil)

    // 启动一个Gin的接口
    router := gin.Default()
    router.GET("/user/info", func(c *gin.Context) {
        type request struct {
            Type interface{} `json:"type" form:"type"`
        }

        r := &request{}
        if err := c.Bind(r); err != nil {
            t.Fatal(err)
        }

        b, _ := json.Marshal(r)
        t.Log(string(b))
    })
    
    // 使用Gin的服务
    rr := httptest.NewRecorder()
    router.ServeHTTP(rr, req)
}

在使用gin进行绑定的时候,返回了错误Unknown type

咱们对代码进行简单的封装,这样就能够提供一个统一的测试方法,咱们对服务进行封装

// 将sever的部分抽象出来
func server(w *httptest.ResponseRecorder, r *http.Request) {
    router := gin.Default()
    router.GET("/user/info", func(c *gin.Context) {
        type request struct {
            Type interface{} `json:"type" form:"type"`
        }

        r := &request{}
        if err := c.Bind(r); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"type": 0})
            return
        }

        b, _ := json.Marshal(r)
        c.JSON(http.StatusOK, gin.H{"type": string(b)})
    })

    router.ServeHTTP(w, r)
}

咱们对请求进行封装:

func TestRequest(t *testing.T) {
    req, _ := http.NewRequest("GET", "/user/info?type=1", nil)

    // 使用Gin的服务
    rr := httptest.NewRecorder()
    server(rr, req)

    p, err := ioutil.ReadAll(rr.Body)
    if err != nil {
        t.Fatal(err)
    }
    t.Log(string(p))
}
相关文章
相关标签/搜索