如何对Go Gin处理程序函数进行单元测试?

我有一个这样的控制器功能....


func GetMaterialByFilter(c *gin.Context) {


    queryParam := weldprogs.QueryParam{}

    c.BindQuery(&queryParam)

    materialByFilter, getErr := services.WeldprogService.GetMaterialByFilter(&queryParam)

    if getErr != nil {

        //TODO : Handle user creation error

        c.JSON(getErr.Status, getErr)

        return

    }

    c.JSON(http.StatusOK, materialByFilter)


}

QueryParam Struct 是这样的。


type QueryParam struct {

    Basematgroup_id []string `form:"basematgroup_id"`

    License_id      []string `form:"license_id"`

    Diameter_id     []string `form:"diameter_id"`

    Gasgroup_id     []string `form:"gasgroup_id"`

    Wiregroup_id    []string `form:"wiregroup_id"`

    Wiremat_id      []string `form:"wiremat_id"`

}

我的测试函数是这样的。


func TestGetMaterialByFilter(t *testing.T) {

    w := httptest.NewRecorder()

    c, _ := gin.CreateTestContext(w)

    GetMaterialByFilter(c)

    assert.Equal(t, 200, w.Code) 


    var got gin.H

    err := json.Unmarshal(w.Body.Bytes(), &got)

    if err != nil {

        t.Fatal(err)

    }

    fmt.Println(got)

    assert.Equal(t, got, got) 

}

在运行此测试时,它给我以下错误


panic: runtime error: invalid memory address or nil pointer dereference [recovered]

        panic: runtime error: invalid memory address or nil pointer dereference

[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x97f626]

但是当我在控制器函数中注释掉c.BindQuery()行时,它成功运行了我的测试函数。我在这里做错了什么?我可以以某种方式模拟c.BindQuery函数吗?


宝慕林4294392
浏览 145回答 1
1回答

茅侃侃

要测试涉及 HTTP 请求的操作,您必须实际初始化 a 并将其设置为 Gin 上下文。要专门测试,正确初始化请求和:*http.Requestc.BindQueryURLURL.RawQueryfunc mockGin() (*gin.Context, *httptest.ResponseRecorder) {    w := httptest.NewRecorder()    c, _ := gin.CreateTestContext(w)    // test request, must instantiate a request first    req := &http.Request{        URL:    &url.URL{},        Header: make(http.Header), // if you need to test headers    }    // example: req.Header.Add("Accept", "application/json")    // request query    testQuery := weldprogs.QueryParam{/* init fields */}    q := req.URL.Query()    for _, s := range testQuery.Basematgroup_id {        q.Add("basematgroup_id", s)    }    // ... repeat for other fields as needed    // must set this, since under the hood c.BindQuery calls    // `req.URL.Query()`, which calls `ParseQuery(u.RawQuery)`    req.URL.RawQuery = q.Encode()        // finally set the request to the gin context    c.Request = req    return c, w}如果需要模拟 JSON 绑定,请参阅此答案。无法按原样测试服务调用。要具有可测试性,它必须(理想情况下)是一个接口,并以某种方式注入作为处理程序的依赖项。services.WeldprogService.GetMaterialByFilter(&queryParam)假设它已经是一个接口,要使其可注入,您要么需要它作为处理程序参数 - 但这迫使您更改处理程序的签名 - 或者将其设置为Gin上下文值:func GetMaterialByFilter(c *gin.Context) {    //...    weldprogService := mustGetService(c)    materialByFilter, getErr := weldprogService.GetMaterialByFilter(&queryParam)    // ...}func mustGetService(c *gin.Context) services.WeldprogService {    svc, exists := c.Get("svc_context_key")    if !exists {        panic("service was not set")    }    return svc.(services.WeldprogService)}然后,您可以在单元测试中模拟它:type mockSvc struct {}// have 'mockSvc' implement the interface func TestGetMaterialByFilter(t *testing.T) {    w := httptest.NewRecorder()    c, _ := gin.CreateTestContext(w)    // now you can set mockSvc into the test context    c.Set("svc_context_key", &mockSvc{})    GetMaterialByFilter(c)    // ... }
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go