Golang中HTTP服务器的测试指南
Golang中HTTP服务器的测试指南 你好,我刚开始学习Go语言,正在寻求关于测试HTTP服务器实现的指导。
我想要测试的行为:
- 在/mypath上收到请求
- 方法必须是POST;所有其他方法返回405错误
- 请求必须包含有效的JSON正文
- 缺少正文发送422
- 缺少必需字段发送422
- 请求必须包含有效的JSON正文
- 方法必须是POST;所有其他方法返回405错误
这些行为是我自己编写的。可能我对RESTful最佳实践的理解有误。我想避免由于不准确的状态码而导致的API客户端实现困扰。我希望我的API用户能够快速理解他们的客户端出了什么问题,我相信准确的状态码将有助于这一点。
我的测试如下:
func TestServer(t *testing.T) {
server := NewServer()
t.Run("Send GET, expect 405", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/myPath", nil)
response := httptest.NewRecorder()
server.ServeHTTP(response, request)
assertResponseStatus(t, got, http.StatusMethodNotAllowed)
})
t.Run("Send POST without body, expect 422", func(t *testing.T) {
var body = []byte(``)
request, _ := http.NewRequest(http.MethodPost, "/myPath", nil)
response := httptest.NewRecorder()
request.Header.Set("Content-Type", JSONContentType)
server.ServeHTTP(response, request)
assertResponseStatus(t, got, http.StatusUnprocessableEntity)
})
}
我意识到这里有一些重复,我还没有进行重构。我也没有为"所有其他方法返回405"编写测试,目前只有GET。我暂时不会涉及"缺少必需字段发送422"。
我正在使用gorilla/mux。
"方法必须是POST,所有其他方法返回405"通过以下方式实现:
router.Handle("/mypath", http.HandlerFunc(myPathHandler)).Methods("POST")
"发送没有正文的POST,期望422"是我遇到问题的地方。
如何构建一个空正文的请求?我尝试过:
request, _ := http.NewRequest(http.MethodPost, "/mypath", nil)
但在尝试时会导致空指针:
func myPathHandler(w http.ResponseWriter, r *http.Request) {
myPathOpts := &myPathRequestBody{}
err := json.NewDecoder(r.Body).Decode(myPathOpts)
...
}
所以我尝试:
func myPathHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
switch {
case err == io.EOF:
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
case err != nil:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
...
}
但这也是一个空指针。
我以不同的方式构建请求:
request, _ := http.NewRequest(http.MethodPost, "/myPath", bytes.NewBuffer(nil))
或者
var body = []byte(``)
request, _ := http.NewRequest(http.MethodPost, "/myPath", bytes.NewBuffer(body))
但我没有得到io.EOF或err != nil。
添加一个switch case来测试body == nil:
func myPathHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
switch {
case body == nil:
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
case err == io.EOF:
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
case err != nil:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
...
}
那里也没有成功。
天啊,我的问题似乎有点冗长。感谢您的时间。
非常感谢您的指导和批评。
诚挚地
更多关于Golang中HTTP服务器的测试指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我不太确定,但422错误表示你期望特定的JSON结构,但实际却收到了空字符串或错误的JSON结构。比如你可能期望:
{"user": "Alex", "email": "alex@gmail.com"}
但却收到了空字符串或:
[1, 3, 44, "cat"]
虽然这是有效的JSON,但结构与你预期的不符。POST请求始终会有请求体,即使长度为零字节。
https://tools.ietf.org/html/rfc4918#section-11.2
422(无法处理的实体)状态码表示服务器理解请求实体的内容类型(因此不适合返回415不支持的媒体类型状态码),且请求实体的语法正确(因此不适合返回400错误请求状态码),但无法处理其中包含的指令。例如,当XML请求体包含格式正确(即语法正确)但语义错误的XML指令时,就可能出现此错误情况。
所以应该是:
更多关于Golang中HTTP服务器的测试指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
go version go1.11.2 linux/amd64
感谢您抽空回复。
我不理解为什么 err == io.EOF 不匹配。我阅读了源码中的注释,但显然遗漏了某些内容。我只是好奇我的理解偏差在哪里。
// Body 是请求的正文。 // // 对于客户端请求,nil 正文表示请求没有正文,例如 GET 请求。HTTP 客户端的传输负责调用 Close 方法。 // // 对于服务器请求,请求正文始终为非 nil,但在没有正文时会立即返回 EOF。服务器将关闭请求正文。ServeHTTP 处理程序不需要关闭。 Body io.ReadCloser
func myPathHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
switch {
case err == io.EOF:
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
case err != nil:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
...
}
我通过检查 len(body) == 0 得到了想要的结果。
参见 使用空正文处理 POST 请求,返回 422 · GitHub
在代码片段中您会看到只有一个测试。这是带有显式空正文的测试。我无法让带有 nil 正文的测试运行而不出现空指针异常。
t.Run("Send POST with nil body, expect 422", func(t *testing.T) {
request := newRequest(t, http.MethodPost, "/", nil)
response := httptest.NewRecorder()
server.ServeHTTP(response, request)
got := response.Code
assertResponseStatus(t, got, http.StatusUnprocessableEntity)
})
我想这只是一个糟糕的测试。


