【问题标题】:How to Dynamically Change Method Receiver如何动态更改方法接收器
【发布时间】:2021-09-18 02:59:09
【问题描述】:

我最近了解到你可以在 go 中执行此操作:

type Env struct{}

func (e *Env) httpHandler(w http.ResponseWriter, r *http.Request) {
   //...
}

func main() {
    // ...
    e := &Env{}
    router := mux.NewRouter()
    router.HandleFunc("/foo", e.httpHandler)
}

这非常适合依赖注入,因为当我进行单元测试时,我可以简单地使用模拟环境调用 httpHandler

但是,我的问题是……说你有:

method := e.httpHandler

e.httpHandler 已经通过反射或其他方式存储到method 之后,有什么方法可以动态更改接收者e 的值?我可以更改传递给method() 的参数,但似乎接收器值被锁定,更改接收器的唯一方法是执行e2.httpHandler。在我的情况下这是不可能的,因为我正在从多路复用路由器中提取 e.httpHandler,我只想在调用 httpHandler 之前将 e 换成不同的接收器。

为了获得更多上下文,我使用router.Walk() 基本上进行表驱动测试,其中我遍历每条路由,调用处理程序,检查返回的响应是否正确,等等。但是,有些路由需要稍微不同的数据库模拟比其他人多,因此对路由器中的所有路由使用一刀切的模拟接收器并不理想。我想用自定义模拟环境为选择处理程序动态换出处理程序接收器。

【问题讨论】:

  • 如果不直接访问接收器,则无法交换或修改方法值的接收器。
  • 您可以对需要不同依赖项的路由进行分组。喜欢Env1 -> router1Env2 -> router2。在测试Env1 时,跳过应该在Env2 中测试的路由。反之亦然。
  • 我认为您应该展示您的测试代码,以便我们了解您在哪里阻塞。
  • 您是否尝试对整个测试套件使用相同的mux.Router 的约束?

标签: go mux


【解决方案1】:

这是我的想法。你怎么看?

type Env struct{
    db string // should be database, just example
}


func (e *Env) httpHandler(w http.ResponseWriter, r * http.Request) {

}

// In case, create different router by different database.
func newRouter(db string) *mux.Router {
    e := &Env{
        db:db,
    }
    router := mux.NewRouter()
    router.HandleFunc("/foo", e.httpHandler)
    router.HandleFunc("/bar", e.httpHandler)
    return router
}

func TestByDatabaseA(t *testing.T)  {
    r := newRouter("foo")
    r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
        tpl, _ := route.GetPathTemplate()
        if !strings.HasPrefix(tpl,"/foo"){
            return nil
        }
        // run test
        return nil
    })
}

func TestByDatabaseB(t *testing.T)  {
    r := newRouter("bar")
    r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
        tpl, _ := route.GetPathTemplate()
        if !strings.HasPrefix(tpl,"/bar"){
            return nil
        }
        // run test
        return nil
    })
}

【讨论】:

  • 这就是我最终选择的,因为它似乎是唯一的并发安全解决方案
【解决方案2】:

如果您在测试套件的整个生命周期中都需要一个路由器 - 并且由于您无法更改指针接收器或重新注册路由处理程序 - 您可以尝试包装 HandlerFunc 以便稍后更改处理程序函数:

type wrapperHandler struct {
    Fn http.HandlerFunc
}

func (wh *wrapperHandler) HandlerFunc() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        wh.Fn.ServeHTTP(w, r) // can change `Fn` later
    }
}

使用:

// setup
router := mux.NewRouter()
wh := wrapperHandler{} // fill in wh.Fn later
router.HandleFunc("/foo", wh.HandlerFunc())

e := Env{ /*db1*/ }
wh.Fn = e.httpHandler
runTest(router)

e = Env{ /*db2*/ }
wh.Fn = e.httpHandler
runTest(router)

【讨论】:

    猜你喜欢
    • 2016-03-04
    • 2017-04-30
    • 2016-02-28
    • 1970-01-01
    • 2015-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多