🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# httptest - HTTP 测试辅助工具 # 由于 Go 标准库的强大支持,Go 可以很容易的进行 Web 开发。为此,Go 标准库专门提供了 httptest 包专门用于进行 http Web 开发测试。 本节我们通过一个社区帖子的增删改查的例子来学习该包。 ## 简单的 Web 应用 我们首先构建一个简单的 Web 应用。 为了简单起见,数据保存在内存,并且没有考虑并发问题。 ```go // 保存 Topic,没有考虑并发问题 var TopicCache = make([]*Topic, 0, 16) type Topic struct { Id int `json:"id"` Title string `json:"title"` Content string `json:"content"` CreatedAt time.Time `json:"created_at"` } ``` 对于 Topic 的增删改查代码很简单,可以查看[完整代码](code/src/chapter09/httptest/data.go)。 接下来,是通过 http 包来实现一个 Web 应用。 ```go func main() { http.HandleFunc("/topic/", handleRequest) http.ListenAndServe(":2017", nil) } ... ``` `/topic/` 开头的请求都交由 `handleRequest` 处理,它根据不同的 `Method` 执行相应的增删改查,详细代码可以查看 [server.go](code/src/chapter09/httptest/server.go)。 准备好 Web 应用后,我们启动它。 > go run server.go data.go 通过 curl 进行简单的测试: > 增:curl -i -X POST http://localhost:2017/topic/ -H 'content-type: application/json' -d '{"title":"The Go Standard Library","content":"It contains many packages."}' > 查:curl -i -X GET http://localhost:2017/topic/1 > 改:curl -i -X PUT http://localhost:2017/topic/1 -H 'content-type: application/json' -d '{"title":"The Go Standard Library By Example","content":"It contains many packages, enjoying it."}' > 删:curl -i -X DELETE http://localhost:2017/topic/1 ## 通过 httptest 进行测试 上面,我们通过 curl 对我们的 Web 应用的接口进行了测试。现在,我们通过 httptest 进行测试。 我们先测试创建帖子,也就是测试 `handlePost` 函数。 ```go func TestHandlePost(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/topic/", handleRequest) reader := strings.NewReader(`{"title":"The Go Standard Library","content":"It contains many packages."}`) r, _ := http.NewRequest(http.MethodPost, "/topic/", reader) w := httptest.NewRecorder() mux.ServeHTTP(w, r) resp := w.Result() if resp.StatusCode != http.StatusOK { t.Errorf("Response code is %v", resp.StatusCode) } } ``` 首先跟待测试代码一样,配置上路由,对 `/topic/` 的请求都交由 `handleRequest ` 处理。 ```go mux := http.NewServeMux() mux.HandleFunc("/topic/", handleRequest) ``` 因为 `handlePost` 的签名是 `func handlePost(w http.ResponseWriter, r *http.Request) error`,为了测试它,我们必须创建 `http.ResponseWriter` 和 `http.Request` 的实例。 接下来的代码就是创建一个 `http.Request` 实例 和 一个 `http.ReponseWriter` 的实例。这里的关键是,`httptest` 为我们提供了一个 `http.ReponseWriter` 接口的实现结构:`httptest.ReponseRecorder`,通过它可以得到一个 `http.ReponseWriter`。 ```go reader := strings.NewReader(`{"title":"The Go Standard Library","content":"It contains many packages."}`) r, _ := http.NewRequest(http.MethodPost, "/topic/", reader) w := httptest.NewRecorder() ``` 准备好之后,可以测试目标函数了。这里,我们没有直接调用 `handlePost(w, r)`,而是调用 `mux.ServeHTTP(w, r)`,实际上这里直接调用 `handlePost(w, r)` 也是可以的,但调用 `mux.ServeHTTP(w, r)` 更完整的测试了整个流程。`mux.ServeHTTP(w, r)` 最终会调用 `handlePost(w, r)`。 最后,通过 `go test -v` 运行测试。 查、改和删帖子的接口测试代码类似,比如,`handleGet` 的测试代码如下: ```go func TestHandleGet(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/topic/", handleRequest) r, _ := http.NewRequest(http.MethodGet, "/topic/1", nil) w := httptest.NewRecorder() mux.ServeHTTP(w, r) resp := w.Result() if resp.StatusCode != http.StatusOK { t.Errorf("Response code is %v", resp.StatusCode) } topic := new(Topic) json.Unmarshal(w.Body.Bytes(), topic) if topic.Id != 1 { t.Errorf("Cannot get topic") } } ``` *注意:因为数据没有落地存储,为了保证后面的测试正常,请将 `TestHandlePost` 放在最前面。* ## 测试代码改进 细心的朋友应该会发现,上面的测试代码有重复,比如: ```go mux := http.NewServeMux() mux.HandleFunc("/topic/", handleRequest) ``` 还有:`w := httptest.NewRecorder()`。 这正好是前面学习的 `setup` 可以做的事情,因此可以使用 `TestMain` 来做重构。 ```go var w *httptest.ResponseRecorder func TestMain(m *testing.M) { http.DefaultServeMux.HandleFunc("/topic/", handleRequest) w = httptest.NewRecorder() os.Exit(m.Run()) } ``` # 导航 # - 上一节:[testing - 其他功能](09.5.md) - 下一节:[总结](09.7.md)