【问题标题】:golang unit test for http handler with custom ServeHTTP implementation具有自定义 ServeHTTP 实现的 http 处理程序的 golang 单元测试
【发布时间】:2018-06-21 18:37:19
【问题描述】:

我正在尝试为我的 http 文件服务器编写单元测试。 我已经实现了 ServeHTTP 函数,以便它将 URL 中的“//”替换为“/”:

type slashFix struct {
    mux http.Handler
}

func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
    h.mux.ServeHTTP(w, r)
}

最基本的代码如下所示:

func StartFileServer() {
    httpMux := http.NewServeMux()
    httpMux.HandleFunc("/abc/", basicAuth(handle))
    http.ListenAndServe(":8000", &slashFix{httpMux})
}

func handle(writer http.ResponseWriter, r *http.Request) {
    dirName := "C:\\Users\\gayr\\GolandProjects\\src\\NDAC\\download\\"    
    http.StripPrefix("/abc",
        http.FileServer(http.Dir(dirName))).ServeHTTP(writer, r)
}

func basicAuth(handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user, pass, ok := r.BasicAuth()
        if user != "UserName" || pass != "Password" {
            w.WriteHeader(401)
            w.Write([]byte("Unauthorised.\n"))
            return
        }
        handler(w, r)
    }
}

我遇到了如下实例来测试 http 处理程序:

req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
if err != nil {
    t.Fatal(err)
}
req.SetBasicAuth("UserName", "Password")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(basicAuth(handle))
handler.ServeHTTP(rr, req)

这样做会调用使用 http.HandleFunc 实现的 ServeHTTP 函数,但我希望调用在我的代码中实现的 ServeHTTP。如何做到这一点?另外,有没有办法让我直接测试 StartFileServer()?

编辑:我检查了 cmets 中提供的链接;我的问题似乎不是重复的。我有一个具体的问题:我希望调用在我的代码中实现的 ServeHTTP,而不是调用使用 http.HandleFunc 实现的 ServeHTTP 函数。我没有在提供的链接中看到这个问题。

【问题讨论】:

  • 我检查了上面提供的链接;我的问题似乎不是重复的。我有一个具体的问题:我希望调用在我的代码中实现的 ServeHTTP,而不是调用使用 http.HandleFunc 实现的 ServeHTTP 函数。
  • 回答完毕。只需传递您的 slashFix 类型的实例,因为它已经是 http.Handler
  • 传递 slashFix 的实例(遵循这个答案:stackoverflow.com/a/37549527/5772695)调用我实现的 ServeHTTP,但我如何将它与文件服务部分的 basicAuth(handle) 结合起来?没有这个,我得到“恐慌:运行时错误:无效的内存地址或零指针取消引用”
  • basicAuth 应该使用http.Handler 而不是http.HandlerFunc,这样会很容易。

标签: unit-testing http go


【解决方案1】:

http.HandlerFunc 实现了 http.Handler。正如 Flimzy 在 cmets 中指出的那样,basicAuth 不需要 HandlerFunc。任何 http.Handler 都可以。坚持使用 http.Handler 接口而不是具体的 HandlerFunc 类型将使一切都易于组合:

func basicAuth(handler http.Handler) http.Handler { // Note: http.Handler, not http.HandlerFunc
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                user, pass, ok := r.BasicAuth()
                if !ok {
                        // TODO
                }
                if user != "UserName" || pass != "Password" {
                        w.WriteHeader(401)
                        w.Write([]byte("Unauthorised.\n"))
                        return
                }

                handler.ServeHTTP(w, r)
        })
}

func TestFoo(t *testing.T) {
        req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
        if err != nil {
                t.Fatal(err)
        }
        req.SetBasicAuth("UserName", "Password")
        rr := httptest.NewRecorder()

        // composition is trivial now
        sf := &slashFix{
                mux: http.HandlerFunc(handle),
        }
        handler := basicAuth(sf)
        handler.ServeHTTP(rr, req)

        // assert correct rr
}

【讨论】:

  • 我不确定我是否理解您想要的测试用例,但由于一切都是 http.Handler 现在您可以随意将处理程序插入在一起。
  • 这很有帮助,谢谢!当同时存在 http.Handler 和 http.HandleFunc 时,我感到很困惑。现在,很清楚了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-24
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多