首页 > 解决方案 > 如何模拟 gin.Context?

问题描述

嗨,我一直在尝试模拟 gin.Context 但我无法让它工作我正在尝试他们在这个解决方案中所做的但它不适用于我的路由器这是我一直得到的错误

r.POST("/urls", urlRepo.CreateUrl)

cannot use urlRepo.CreateUrl (value of type func(c controllers.Icontext)) as gin.HandlerFunc value in argument to r.POSTcompilerIncompatibleAssign

这是我为稍后模拟而创建的接口以及我将在其中进行测试的方法

type Icontext interface {
  BindJSON(obj interface{}) error
  JSON(code int, obj interface{})
  AbortWithStatus(code int)
  AbortWithStatusJSON(code int, jsonObj interface{})

}

func (repository *UrlRepo) CreateUrl(c Icontext) {
    var url models.Url
    c.BindJSON(&url)
    if !validators.IsCreateJsonCorrect(url) {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Error format in Short or Full"})
        return
    }
    err := repository.reposito.CreateUrl(repository.Db, &url)
    if err != nil {
        c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err})
        return
    }
    c.JSON(http.StatusOK, url)
} 

代替

func (repository *UrlRepo) CreateUrl(c Icontext)

它是

func (repository *UrlRepo) CreateUrl(c *gin.Context) 

标签: gotestingmockinggo-gin

解决方案


严格来说,您不能*gin.Context以一种有意义的方式“模拟” a,因为它struct具有未导出的字段和方法。

此外,您不能传递给r.POST()类型不是 a 的函数gin.HandlerFunc,定义为func(*gin.Context). 您的处理程序的类型CreateUrl(c Icontext)根本不匹配。

如果您的目标是对 Gin 处理程序进行单元测试,那么您绝对不必模拟*gin.Context. 您应该做的是在其中设置测试值。为此,您可以简单地使用gin.CreateTestContext()并手动初始化其中的一些字段。更多信息 在这里

如果出于某种其他原因,您的目标是提供一种功能的替代实现,*gin.Context以便在您的处理程序中使用,您可以做的是使用您自己的替代方法定义您自己的类型并将其嵌入*gin.Context其中。

在实践中:

type MyGinContext struct {
    *gin.Context
}

func (m *MyGinContext) BindJSON(obj interface{}) error {
    fmt.Println("my own BindJSON")
    return m.Context.BindJSON(obj) // or entirely alternative implementation
}

// Using the appropriate function signature now
func (repository *UrlRepo) CreateUrl(c *gin.Context) {
    myCtx := &MyGinContext{c}

    var url models.Url
    _ = myCtx.BindJSON(&url) // will also print "my own BindJSON"
    // ...

    // other gin.Context methods are promoted and available on MyGinContext
    myCtx.Status(200)
} 

但老实说,我不确定你为什么要覆盖*gin.Context. 如果你想提供不同的绑定逻辑,甚至不同的渲染,你可以实现库已经暴露的接口。例如:

实现绑定:

c.ShouldBindWith()binding.Binding将您可以实现的接口作为第二个参数:

type MyBinder struct {
}

func (m *MyBinder) Name() string {
    return "foo"
}

func (m *MyBinder) Bind(*http.Request, interface{}) error {
    // stuff
    return nil
}

func MyHandler(c *gin.Context) {
   var foo struct{/*fields*/}
   c.ShouldBindWith(&foo, &MyBinder{})
}

实现一个渲染器:

type MyRenderer struct {
}

type Render interface {
func (m *MyRenderer) Render(http.ResponseWriter) error {
    // ...
    return nil
}

func (m *MyRenderer) WriteContentType(w http.ResponseWriter) {
    header := w.Header()
    if val := header["Content-Type"]; len(val) == 0 {
        header["Content-Type"] = "application/foo+bar"
    }
}

func MyHandler(c *gin.Context) {
   c.Render(200, &MyRenderer{})
}

推荐阅读