如何为 BindJSON 设置模拟 gin.Context [英] How to set mock gin.Context for BindJSON

查看:75
本文介绍了如何为 BindJSON 设置模拟 gin.Context的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 Go 中设置测试.

I'm setting up testing in Go.

我使用 go-sqlmock 来测试 mysql 连接和 Go Gin 作为框架.现在我尝试测试mysql插入逻辑.

I use go-sqlmock to test mysql connection and Go Gin as framework. Now I try to test mysql insert logic.

问题是我需要设置模拟 gin.Context 稍后用于 BindJSON.
但到目前为止我无法设置这个 gin.Context.

The problem is I need to set mock gin.Context which is used for BindJSON later.
But I can't set this gin.Context so far.

server side: golang
db: mysql
web framework: gin

dao.go

unc PostImageToDBDao(c *gin.Context, db *sql.DB) {
        // Because of this logic, I need to set gin.Context with json
    var imageData util.ImageData
    c.BindJSON(&imageData)

    for _, imageName := range imageData.IMAGENAMES {
        ins, err := db.Prepare("INSERT INTO images(article_uuid, image_name) VALUES(?,?)")
        if err != nil {
            log.Fatal(err)
        }
        ins.Exec(imageData.ARTICLEUUID, imageName.NAME)
    }
}

dao_test.go

dao_test.go

func TestPostImageToDBDao(t *testing.T) {
    db, mock, err := sqlmock.New()

    if err != nil {
        t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
    }

    defer db.Close()

    prep := mock.ExpectPrepare("^INSERT INTO images*")

    prep.ExpectExec().
        WithArgs("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "b8119536-fad5-4ffa-ab71-2f96cca19697").
        WillReturnResult(sqlmock.NewResult(1, 1))

        // try to set context with json post
    req, _ := http.NewRequest("POST", "/post/image/db", bytes.NewBuffer([]byte(`[{
        "articleUUID": "bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c",
        "imageNames": "b8119536-fad5-4ffa-ab71-2f96cca19697",
      }]`)))
    req.Header.Set("Content-Type", "application/json")
    var context *gin.Context
    context = &gin.Context{Request: req}

    PostImageToDBDao(context, db)

    if err := mock.ExpectationsWereMet(); err != nil {
        t.Errorf("there were unfulfilled expections: %s", err)
    }
}

我希望模拟 gin.Context 设置正确并运行 go test -v 没有错误,但是它失败并出现以下错误:

I expect mock gin.Context to set properly and run go test -v without error, however it fails with the following error:

=== RUN   TestPostImageToDBDao
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 0 with 400
--- FAIL: TestPostImageToDBDao (0.00s)
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=0x60 pc=0x15bde75]

goroutine 50 [running]:
testing.tRunner.func1(0xc000234100)
        /usr/local/go/src/testing/testing.go:830 +0x392
panic(0x16918e0, 0x1ce5850)
        /usr/local/go/src/runtime/panic.go:522 +0x1b5
github.com/gin-gonic/gin.(*Context).AbortWithStatus(0xc00026aee8, 0x190)
        /Users/jpskgc/go/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:146 +0x45
github.com/gin-gonic/gin.(*Context).AbortWithError(0xc0000d9ee8, 0x190, 0x1863e00, 0xc0002700a0, 0x1863e00)
        /Users/jpskgc/go/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:162 +0x39
github.com/gin-gonic/gin.(*Context).MustBindWith(0xc00026aee8, 0x16328e0, 0xc00022c180, 0x186e060, 0x1d16588, 0x1e316d0, 0x0)
        /Users/jpskgc/go/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:561 +0x92
github.com/gin-gonic/gin.(*Context).BindJSON(...)
        /Users/jpskgc/go/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:528
article/api/dao.PostImageToDBDao(0xc00026aee8, 0xc000276000)
        /Users/jpskgc/article/api/dao/dao.go:54 +0x87
article/api/dao.TestPostImageToDBDao(0xc000234100)
        /Users/jpskgc/article/api/dao/dao_test.go:204 +0x4b6
testing.tRunner(0xc000234100, 0x17897e0)
        /usr/local/go/src/testing/testing.go:865 +0xc0
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:916 +0x35a
exit status 2
FAIL    article/api/dao 0.032s

推荐答案

首先,你必须实例化一个测试 *gin.Context 并确保它的 *http.Request非零:

First, you must instantiate a test *gin.Context and make sure its *http.Request is non-nil:

    w := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(w) 

    c.Request = &http.Request{
        Header: make(http.Header),
    }

然后您可以通过以下方式模拟 POST json 正文:

Then you can mock a POST json body in the following way:

func MockJsonPost(c *gin.Context /* the test context */, content interface{}) {
    c.Request.Method = "POST" // or PUT
    c.Request.Header.Set("Content-Type", "application/json")

    jsonbytes, err := json.Marshal(jsonMap)
    if err != nil {
        panic(err)
    }
    
    // the request body must be an io.ReadCloser
    // the bytes buffer though doesn't implement io.Closer,
    // so you wrap it in a no-op closer
    c.Request.Body = io.NopCloser(bytes.NewBuffer(jsonbytes))
}

其中函数参数 content interface{} 是可以使用 json.Marshal(),所以在大多数情况下是一个带有适当 json 标签的结构,或者一个 map[string]接口{}.

where the function argument content interface{} is anything that can be marshalled into JSON with json.Marshal(), so in most cases a struct with the proper json tags, or a map[string]interface{}.

示例用法:

func TestMyHandler(t *testing.T) {
    w := httptest.NewRecorder()
    ctx, _ := gin.CreateTestContext(w) 

    ctx.Request = &http.Request{
        Header: make(http.Header),
    }


    MockJsonPost(ctx, map[string]interface{}{"foo": "bar"})
    
    MyHandler(ctx)
    assert.EqualValues(t, http.StatusOK, w.Code)
} 

相关:

这篇关于如何为 BindJSON 设置模拟 gin.Context的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆