为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
宝慕林4294392 2022-08-24 11:27:18
我有一个这样的控制器功能....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函数吗?
查看完整描述

1 回答

?
茅侃侃

TA贡献1842条经验 获得超22个赞

要测试涉及 HTTP 请求的操作,您必须实际初始化 a 并将其设置为 Gin 上下文。要专门测试,正确初始化请求和:*http.Requestc.BindQueryURLURL.RawQuery


func 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)

    // ... 

}


查看完整回答
反对 回复 2022-08-24
  • 1 回答
  • 0 关注
  • 144 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号