如何测试 HTTP 请求处理程序

huangapple go评论63阅读模式
英文:

How to test http request handlers

问题

我有一组请求处理程序,就像下面这个例子:

func GetProductsHandler(w http.ResponseWriter, req *http.Request) {
    defer req.Body.Close()
    products := db.GetProducts()

    // ...
    // 将产品作为 JSON 数组返回
}

我该如何正确地测试它们?我应该向函数发送模拟的 ResponseWriter 和 Request 对象,并查看结果吗?

在 Go 中,是否有工具可以模拟请求和响应对象,以简化测试过程,而无需在测试之前启动服务器?

英文:

I have a set of requests handlers like the one below:

func GetProductsHandler(w http.ResponseWriter, req *http.Request) {
    defer req.Body.Close()
    products := db.GetProducts()

    //    ...
    // return products as JSON array
}

How do I test them the right way? Should I send mock ResponseWriter and Request objects to the function and see the results?

Are there tools to mock request and response objects in Go to simplify the process without having to start server before testing?

答案1

得分: 8

Go提供了一个用于测试处理程序的模拟写入器。标准库文档提供了一个示例:

package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
)

func main() {
	handler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "something failed", http.StatusInternalServerError)
	}

	req := httptest.NewRequest("GET", "http://example.com/foo", nil)
	w := httptest.NewRecorder()
	handler(w, req)

	fmt.Printf("%d - %s", w.Code, w.Body.String())
}

我认为全局依赖(db)会给清晰的单元测试带来麻烦。使用Go,你的测试可以重新分配一个值,掩盖全局的db值。

另一种策略(我更喜欢的)是将处理程序封装在一个结构体中,该结构体具有一个db属性。

type Handlers struct {
  db DB_INTERFACE
}

func (hs *Handlers) GetProductsHandler(w http.ResponseWriter, req *http.Request) {...}

这样,你的测试可以使用一个存根db对象实例化一个Handlers,从而允许你创建无IO的单元测试。

英文:

Go provides a mock writer for use in testing handlers. The standard library documentation provides an example:

package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
)

func main() {
	handler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "something failed", http.StatusInternalServerError)
	}

	req := httptest.NewRequest("GET", "http://example.com/foo", nil)
	w := httptest.NewRecorder()
	handler(w, req)

	fmt.Printf("%d - %s", w.Code, w.Body.String())
}

I think having a global dependency (db) throws a wrench into clean unit testing. Using go your test could reassign a value, masking, the global value of db.

Another strategy (my preferred) is to package your handler in a struct, which has a db attribute..

type Handlers struct {
  db DB_INTERFACE
}

func (hs *Handlers) GetProductsHandler(w http.ResponseWriter, req *http.Request) {...}

This way your test can instantiate a Handlers with a stub db object which will allow you to create IO free unit tests.

huangapple
  • 本文由 发表于 2016年11月18日 23:17:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/40680498.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定