如何动态更改方法接收器
How to Dynamically Change Method Receiver
我最近了解到您可以在 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
。这在我的情况下是不可能的,因为我正在从 mux 路由器中提取 e.httpHandler
并且我只想在调用 httpHandler
.[=22= 之前用不同的接收器换出 e
]
为了了解更多上下文,我正在使用 router.Walk()
来执行 table 驱动测试,我在其中遍历每条路线,调用处理程序,检查返回的响应是否正确,等等。但是,某些路由需要的数据库模拟与其他路由略有不同,因此对路由器中的所有路由使用通用的模拟接收器并不理想。我想用 select 处理程序的自定义模拟环境动态交换处理程序接收器。
这是我的想法。你怎么看?
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
})
}
如果您在测试套件的整个生命周期中都需要一个路由器 - 并且由于您无法更改指针接收器或重新注册路由处理程序 - 您可以尝试包装 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)
我最近了解到您可以在 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
。这在我的情况下是不可能的,因为我正在从 mux 路由器中提取 e.httpHandler
并且我只想在调用 httpHandler
.[=22= 之前用不同的接收器换出 e
]
为了了解更多上下文,我正在使用 router.Walk()
来执行 table 驱动测试,我在其中遍历每条路线,调用处理程序,检查返回的响应是否正确,等等。但是,某些路由需要的数据库模拟与其他路由略有不同,因此对路由器中的所有路由使用通用的模拟接收器并不理想。我想用 select 处理程序的自定义模拟环境动态交换处理程序接收器。
这是我的想法。你怎么看?
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
})
}
如果您在测试套件的整个生命周期中都需要一个路由器 - 并且由于您无法更改指针接收器或重新注册路由处理程序 - 您可以尝试包装 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)