【问题标题】:How to mock a gin.Context?如何模拟 gin.Context?
【发布时间】:2021-08-03 02:53:42
【问题描述】:

您好,我一直在尝试模拟 gin.Context,但无法使其工作 我正在尝试他们在solution 中所做的事情,但它不适用于我的路由器这是我遇到的错误

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) 

【问题讨论】:

    标签: go testing mocking go-gin


    【解决方案1】:

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

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

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

    如果出于其他原因,您的目标是提供*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{})
    }
    

    【讨论】:

      【解决方案2】:

      如果您使用gin-gonic 作为您的http 路由器,您的入口点的参数应该是*gin.Context

      因此,例如,您应该替换这个:

      func (repository *UrlRepo) CreateUrl(c Icontext) {
      

      有了这个

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

      通过这种方式,您应该能够使用模拟 gin 上下文作为单元测试的参数

      【讨论】:

      猜你喜欢
      • 2020-01-04
      • 2017-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-07
      • 2014-11-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多