如何模拟 gin.Context? [英] How to mock a gin.Context?

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

问题描述

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

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

不能在 r.POSTcompilerIncompatibleAssign 的参数中使用 urlRepo.CreateUrl(func(c controllers.Icontext) 类型的值)作为 gin.HandlerFunc 值

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

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

}

func (repository *UrlRepo) CreateUrl(c Icontext) {var url 模型.Urlc.BindJSON(&url)如果 !validators.IsCreateJsonCorrect(url) {c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Error format in Short or Full"})返回}错误 := repository.reposito.CreateUrl(repository.Db, &url)如果错误!= nil {c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{error": err})返回}c.JSON(http.StatusOK, url)}

代替

func (repository *UrlRepo) CreateUrl(c Icontext)

原来是

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

解决方案

严格来说,你不能mock";*gin.Context 以一种有意义的方式,因为它是一个带有未导出的字段和方法的 struct.

此外,您不能将类型不是 gin.HandlerFunc 的函数传递给 r.POST(),定义为 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{}) 错误 {fmt.Println(我自己的 BindJSON")return m.Context.BindJSON(obj)//或完全替代的实现}//现在使用适当的函数签名func (repository *UrlRepo) CreateUrl(c *gin.Context) {myCtx := &MyGinContext{c}var url 模型.Url_ = myCtx.BindJSON(&url)//也会打印我自己的 BindJSON";//...//其他 gin.Context 方法被提升并在 MyGinContext 上可用myCtx.Status(200)}

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

实现绑定:

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

type MyBinder struct {}func (m *MyBinder) Name() string {返回foo"}func (m *MyBinder) Bind(*http.Request, interface{}) 错误 {//东西返回零}func MyHandler(c *gin.Context) {var foo 结构{/*字段*/}c.ShouldBindWith(&foo, &MyBinder{})}

实现渲染器:

type MyRenderer struct {}类型渲染接口{func (m *MyRenderer) Render(http.ResponseWriter) 错误 {//...返回零}func (m *MyRenderer) WriteContentType(w http.ResponseWriter) {标头 := 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{})}

Hi I've been trying to mock a gin.Context but I have not been able to make it work I was trying what they did in this solution but it does not work with my router this is the error I have been getting

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

This is the interface I created to later mock and the method in which I will be testing

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

Instead of

func (repository *UrlRepo) CreateUrl(c Icontext)

it was

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

解决方案

Strictly speaking, you can't "mock" a *gin.Context in a meaningful way, because it's a struct with unexported fields and methods.

Furthermore, you can't pass to r.POST() a function whose type is not a gin.HandlerFunc, defined as func(*gin.Context). The type of your handler CreateUrl(c Icontext) simply doesn't match.

If your goal is to unit test a Gin handler, you definitely don't have to mock the *gin.Context. What you should do is to set test values into it. For that, you can simply use gin.CreateTestContext() and manually init some of it fields. More info here.

If for some other reason, your goal is to provide an alternate implementation of a functionality of *gin.Context for use inside your handler, what you can do is define your own type with your own alternative methods and embed the *gin.Context in it.

In practice:

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

But honestly I'm not sure why you would want to override some methods of the *gin.Context. If you want to provide different binding logic, or even different rendering, you can implement the interfaces that are already exposed by the library. For example:

Implement a binding:

c.ShouldBindWith() takes as second parameter an interface binding.Binding which you can implement:

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{})
}

Implement a renderer:

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{})
}

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

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