Golang中如何使用httptest.NewRequest包含带值的路径?
Golang中如何使用httptest.NewRequest包含带值的路径? 我正在尝试按照《重温HTTP处理器 | main | 通过测试学习Go》中描述的方法测试HTTP处理器。
我有以下代码(为简洁起见已精简):
email := "myemail@test.me"
urlPath := fmt.Sprintf("http://test.com/%s", email)
request := httptest.NewRequest(http.MethodPost, urlPath, nil)
response := httptest.NewRecorder()
handleEmailCheck(response, request)
路由类似于:
router.HandleFunc("POST /users/{email}", handleEmailCheck)
我相信路由/路由器没有被使用,因为我只是在单独测试处理器。我尝试过:
urlPath := fmt.Sprintf("http://test.com/users/%s", email)
urlPath := fmt.Sprintf("/%s", email)
但它总是返回400,因为以下代码失败(返回“”):
email := r.PathValue("email")
如果我运行应用程序并用Postman测试,它工作正常。httptest 是否需要任何特殊要求或设置才能传递路径值?
更多关于Golang中如何使用httptest.NewRequest包含带值的路径?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢你们两位。我已经尝试了两种方法,并且都按预期工作了。不过,我打算采用 @GonzaSaya 的方法,因为在我看来它更简单、更清晰。
更多关于Golang中如何使用httptest.NewRequest包含带值的路径?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
不过,我选择采用 @GonzaSaya 的方法,因为在我看来它更简单、更清晰。
这取决于你要测试什么。启用代码覆盖率并测试两种解决方案,你就会明白我的意思。话虽如此,两种方法都很好,我很高兴你找到了解决方案!
尝试:
r, err := http.NewRequest(method, path, body)
r.SetPathValue("key", "value")
示例位于:
GitHub - saya-ML/golang-test-params
通过创建GitHub账户来为saya-ML/golang-test-params的开发做出贡献。
在测试HTTP处理器时,httptest.NewRequest创建的请求不会自动解析路径参数。你需要手动设置路径值,或者使用路由器来解析。以下是两种解决方案:
方案1:手动设置路径值(推荐用于单元测试)
在测试中直接设置PathValue:
email := "myemail@test.me"
request := httptest.NewRequest(http.MethodPost, "/users/"+email, nil)
// 手动设置路径参数
request.SetPathValue("email", email)
response := httptest.NewRecorder()
handleEmailCheck(response, request)
方案2:使用路由器进行测试(集成测试)
如果你需要测试完整的路由解析,应该使用路由器:
email := "myemail@test.me"
router := http.NewServeMux()
router.HandleFunc("POST /users/{email}", handleEmailCheck)
request := httptest.NewRequest(http.MethodPost, "/users/"+email, nil)
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
完整示例代码
func TestHandleEmailCheck(t *testing.T) {
email := "myemail@test.me"
// 方案1:直接设置路径值
t.Run("unit test with manual path value", func(t *testing.T) {
request := httptest.NewRequest(http.MethodPost, "/users/"+email, nil)
request.SetPathValue("email", email)
response := httptest.NewRecorder()
handleEmailCheck(response, request)
if response.Code != http.StatusOK {
t.Errorf("expected status OK, got %v", response.Code)
}
})
// 方案2:使用路由器
t.Run("integration test with router", func(t *testing.T) {
router := http.NewServeMux()
router.HandleFunc("POST /users/{email}", handleEmailCheck)
request := httptest.NewRequest(http.MethodPost, "/users/"+email, nil)
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
if response.Code != http.StatusOK {
t.Errorf("expected status OK, got %v", response.Code)
}
})
}
处理器示例
func handleEmailCheck(w http.ResponseWriter, r *http.Request) {
email := r.PathValue("email")
if email == "" {
http.Error(w, "email parameter required", http.StatusBadRequest)
return
}
// 处理逻辑
fmt.Fprintf(w, "Processing email: %s", email)
}
使用SetPathValue方法是Go 1.22+中引入的,专门用于在测试中模拟路径参数。对于单元测试处理器逻辑,这是最直接的方法。
我认为路由/路由器没有被使用,因为我只是在单独测试处理器。
确实如此。在你的测试中,你需要做一些事情来设置你的多路复用器/路由。查看 http/httptest 中的示例。这篇帖子展示了如何编写一些测试:
在 Go 中测试你的(HTTP)处理器
在 Go 中构建 Web(HTTP)服务?让我们谈谈如何对你的处理器函数进行单元测试。
另一种方法是将你的多路复用器逻辑拆分出来,然后在测试中直接复用。类似这样:
在 Go 中测试 HTTP 处理器
简介 在这个例子中,我将使用 httptest 库创建单元测试,我们…
采用第二种方案,让我们根据你想要测试的内容创建一个示例:
func main() {
mux := newAppMux()
http.ListenAndServe(":9090", mux)
}
func newAppMux() *http.ServeMux {
router := http.NewServeMux()
router.HandleFunc("POST /users/{email}", handleEmailCheck)
return router
}
func handleEmailCheck(w http.ResponseWriter, r *http.Request) {
email := r.PathValue("email")
if len(email) > 0 {
fmt.Fprintf(w, `{ "email" : "%v" }`, email)
} else {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, http.StatusText(http.StatusNotFound))
}
}
测试方法如下:
func TestHandleEmailCheck(t *testing.T) {
ts := httptest.NewServer(newAppMux())
defer ts.Close()
client := ts.Client()
res, err := client.Post(fmt.Sprintf("%v/users/test@test.com", ts.URL), "application/json", strings.NewReader(`{ "request": "body" }`))
if err != nil {
t.Errorf("Wasn't expecting error. Got: %v", err)
}
resBody, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("Wasn't expecting error. Got: %v", err)
}
expecting := `{ "email" : "test@test.com" }`
if string(resBody) != expecting {
t.Errorf("Unexpected response.\n\nExpected: %v\nGot: %s", expecting, resBody)
}
}
希望这些足以让你开始!

