英文:
Testing handlers that use Gorilla/context
问题
我正在使用gorilla/context在一个Web应用程序中。文档中的示例如下:
func MyHandler(w http.ResponseWriter, r *http.Request) {
//...
val := context.Get(r, foo.MyKey)
//...
}
我该如何对这样的处理程序进行单元测试?到目前为止,我唯一成功的方法是在我的测试中使用context包。我在考虑是否可以将一个context参数注入到处理程序中,但这样我就不符合HandlerFunc接口的规范了。
英文:
I'm using gorilla/context in a web app. The example in the docs looks like:
func MyHandler(w http.ResponseWriter, r *http.Request) {
//...
val := context.Get(r, foo.MyKey)
//...
}
How can I unit test a handler that works like this? The only way I've managed so far is to use the context package inside my tests. I'm thinking at the moment that I could inject a context parameter into the handler but then I'm not conforming to the HandlerFunc interface.
答案1
得分: 1
这是一个经典的横切关注点的例子。
你正在使用第三方工具来神奇地处理你的单元测试的输入参数。根据这个定义,你需要做一些额外的设置来准备所需的context
状态。
对于Go的http处理程序(按照惯例,要保持简单),你不应该需要在函数外部获取额外的数据:将所有需要的数据都保留在单个函数内部。
个人而言,我尽量避免这样破坏我的处理程序。在我构建的几十个大型网站中,我只使用过gorilla的context一次。那基本上是为了绕过缓存的响应,只是为了让数据对最终用户保持更新。在我的单元测试中,我简单地忽略了这一点,因为它超出了我正在测试的范围。
相反,我使用中间件包装器来设置我在处理程序中所需的基本功能,并相应地修改处理程序的签名。
- 缓存
- 日志记录
- 身份验证和授权
- JSON序列化
- 上下文(例如从数据库加载的预期User{}对象)
...等等。当你使用mux
注册处理程序时,我会创建一个中间件来包装它,该中间件使用gorilla/context来查找cookie、userid或其他内容,从缓存、数据库、redis等中获取用户对象,并调用具有以下签名的处理程序:
func MyHandler(u User, p Page, w http.ResponseWriter, r *http.Request) {
// u是从中间件加载的User对象
// p是你的全局Page对象...等等
}
这样,你的单元测试只注入你需要测试的项目。
而你可以在QA服务器上对中间件进行集成测试,使用数据存储中的预期User和Page对象。
英文:
This is a classic cross-cutting concerns example.
You are using a 3rd party to magically handle input params for your unit under test. By that very definition, you are going to have to do some extra setup to prep the context
for the state you want.
When it comes to Go http handlers (which the convention is to KISS it), you shouldn't need to "reach out of context" of your func to get extra data: keep all the data you need within your single func.
Personally, I try to avoid corrupting my handlers like this. I think I've only used gorilla's context once out of the dozens of big sites I've built. And that was basically to get around a cached response, just to keep the data refesh to the end user. Of which I simply ignored in my unit tests, as it was out-of-scrope of what I was testing.
Instead, I use middle-ware wrappers to setup the basics I want in my handles and modify the handler's signature accordingly.
- caching
- logging
- authentication and authorization
- json marshaling
- context (e.g. expected User{} object loaded from DB)
...etc. I would create a middle-ware that wraps your handler when you register it with mux
that uses gorilla/context to lookup your cookie or userid or something, hidrates that user object from cache, DB, redis, etc, and then calls your handler that would have a signature like:
func MyHandler(u User, p Page, w http.ResponseWriter, r *http.Request) {
// u is the User object loaded from middle-ware
// p is your global Page object... etc
}
That way, your unit tests inject only the items you need under test.
And you can integration test your middle-ware on a QA server with expected User and Page objects in a datastore.
答案2
得分: 1
我的团队的做法是在路由处理程序中添加一个名称,然后在测试中通过名称调用该路由。
以下是添加路由的方法:
r.HandleFunc("/<route>", MyHandler).Methods("GET").Name("MyHandlerByName")
然后,以下是测试的方法:
r.Get("MyHandlerByName")
英文:
The way my team does it is to add a name to the route handler and then in the tests we call that route by name.
This is how to add a route:
r.HandleFunc("/<route>", MyHandler).Methods("GET").Name("MyHandlerByName")
Then this is how to test it
r.Get("MyHandlerByName")
答案3
得分: 1
一种测试处理程序的方法是修改它们的创建方式。例如,创建一个返回http.HandlerFunc的函数,这个函数可以有参数。你可以模拟发送给函数的值。
无参数的情况下:
func State() http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// 你的代码
})
}
带参数的情况下:
func State(pParam1, pParam2, pParam3 ...) http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// 使用 pParam1, pParam2, pParam3 的代码
})
}
映射将为:
http.HandleFunc("/State", State())
或者
http.HandleFunc("/State", State(value1, value2, value3 ...))
英文:
One way to test handles is modify the way in which they are created. for example, Creating a function that return a http.HandlerFunc, this function can have parameters. You can mock the values that you send to the function
Without parameters
func State() http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code
})
}
With Parameters
func State(pParam1,pParam2,pParam3 ...) http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code using pParam1,pParam2,pParam3
})
}
The mapping will be
http.HandleFunc("/State", State())
or
http.HandleFunc("/State", State(value1,value2,value3 ....))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论