为了账号安全,请及时绑定邮箱和手机立即绑定

使用 gorilla/mux URL 参数的函数的单元测试

使用 gorilla/mux URL 参数的函数的单元测试

Go
30秒到达战场 2021-11-29 19:10:53
gorilla/mux 过去不提供设置 URL 变量的可能性。现在确实如此,这就是为什么在很长一段时间内,第二高票数的答案都是正确的答案。要遵循的原始问题:这是我想要做的:main.gopackage mainimport (    "fmt"    "net/http"        "github.com/gorilla/mux")    func main() {    mainRouter := mux.NewRouter().StrictSlash(true)    mainRouter.HandleFunc("/test/{mystring}", GetRequest).Name("/test/{mystring}").Methods("GET")    http.Handle("/", mainRouter)        err := http.ListenAndServe(":8080", mainRouter)    if err != nil {        fmt.Println("Something is wrong : " + err.Error())    }}func GetRequest(w http.ResponseWriter, r *http.Request) {    vars := mux.Vars(r)    myString := vars["mystring"]        w.WriteHeader(http.StatusOK)    w.Header().Set("Content-Type", "text/plain")    w.Write([]byte(myString))}这将创建一个基本的 http 服务器侦听端口8080,该端口与路径中给出的 URL 参数相呼应。因此,http://localhost:8080/test/abcd它将写回包含abcd在响应正文中的响应。该GetRequest()函数的单元测试在main_test.go 中:package mainimport (    "net/http"    "net/http/httptest"    "testing"    "github.com/gorilla/context"    "github.com/stretchr/testify/assert")func TestGetRequest(t *testing.T) {    t.Parallel()        r, _ := http.NewRequest("GET", "/test/abcd", nil)    w := httptest.NewRecorder()    //Hack to try to fake gorilla/mux vars    vars := map[string]string{        "mystring": "abcd",    }    context.Set(r, 0, vars)        GetRequest(w, r)    assert.Equal(t, http.StatusOK, w.Code)    assert.Equal(t, []byte("abcd"), w.Body.Bytes())}测试结果是:--- FAIL: TestGetRequest (0.00s)    assertions.go:203:                             Error Trace:    main_test.go:27            Error:      Not equal: []byte{0x61, 0x62, 0x63, 0x64} (expected)                    != []byte(nil) (actual)                        Diff:            --- Expected            +++ Actual            @@ -1,4 +1,2 @@            -([]uint8) (len=4 cap=8) {            - 00000000  61 62 63 64                                       |abcd|            -}            +([]uint8) <nil>                     FAILFAIL    command-line-arguments  0.045s
查看完整描述

3 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

gorilla/mux提供SetURLVars用于测试目的的函数,您可以使用它来注入您的 mock vars。


func TestGetRequest(t *testing.T) {

    t.Parallel()


    r, _ := http.NewRequest("GET", "/test/abcd", nil)

    w := httptest.NewRecorder()


    //Hack to try to fake gorilla/mux vars

    vars := map[string]string{

        "mystring": "abcd",

    }


    // CHANGE THIS LINE!!!

    r = mux.SetURLVars(r, vars)


    GetRequest(w, r)


    assert.Equal(t, http.StatusOK, w.Code)

    assert.Equal(t, []byte("abcd"), w.Body.Bytes())

}


查看完整回答
反对 回复 2021-11-29
?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

问题是,即使您使用0as 值来设置上下文值,mux.Vars()读取的值也不是相同的。mux.Vars()正在使用varsKey(正如您已经看到的)类型contextKey而不是int.


当然,contextKey定义为:


type contextKey int

这意味着它具有 int 作为底层对象,但在比较 go 中的值时,类型会起作用,因此int(0) != contextKey(0).


我不明白你怎么能欺骗大猩猩多路复用器或上下文来返回你的值。


话虽如此,我想到了几种测试方法(请注意,下面的代码未经测试,我直接在此处输入,因此可能存在一些愚蠢的错误):


正如有人建议的那样,运行一个服务器并向它发送 HTTP 请求。

无需运行服务器,只需在测试中使用 gorilla mux Router。在这种情况下,您将有一个传递给 的路由器ListenAndServe,但您也可以在测试中使用相同的路由器实例并调用ServeHTTP它。路由器将负责设置上下文值,它们将在您的处理程序中可用。


func Router() *mux.Router {

    r := mux.Router()

    r.HandleFunc("/employees/{1}", GetRequest)

    (...)

    return r 

}

在 main 函数的某个地方,你会做这样的事情:


http.Handle("/", Router())

在您的测试中,您可以执行以下操作:


func TestGetRequest(t *testing.T) {

    r := http.NewRequest("GET", "employees/1", nil)

    w := httptest.NewRecorder()


    Router().ServeHTTP(w, r)

    // assertions

}

包装您的处理程序,以便它们接受 URL 参数作为第三个参数,并且包装器应调用mux.Vars()URL 参数并将其传递给处理程序。


使用此解决方案,您的处理程序将具有签名:


type VarsHandler func (w http.ResponseWriter, r *http.Request, vars map[string]string)

并且您必须调整对它的调用以符合http.Handler接口:


func (vh VarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    vars := mux.Vars(r)

    vh(w, r, vars)

}

要注册处理程序,您将使用:


func GetRequest(w http.ResponseWriter, r *http.Request, vars map[string]string) {

    // process request using vars

}


mainRouter := mux.NewRouter().StrictSlash(true)

mainRouter.HandleFunc("/test/{mystring}", VarsHandler(GetRequest)).Name("/test/{mystring}").Methods("GET")

您使用哪一种取决于个人喜好。就我个人而言,我可能会选择选项 2 或 3,稍微偏向于选项 3。


查看完整回答
反对 回复 2021-11-29
?
慕后森

TA贡献1802条经验 获得超5个赞

在 golang 中,我的测试方法略有不同。


我稍微重写了你的 lib 代码:


package main


import (

        "fmt"

        "net/http"


        "github.com/gorilla/mux"

)


func main() {

        startServer()

}


func startServer() {

        mainRouter := mux.NewRouter().StrictSlash(true)

        mainRouter.HandleFunc("/test/{mystring}", GetRequest).Name("/test/{mystring}").Methods("GET")

        http.Handle("/", mainRouter)


        err := http.ListenAndServe(":8080", mainRouter)

        if err != nil {

                fmt.Println("Something is wrong : " + err.Error())

        }

}


func GetRequest(w http.ResponseWriter, r *http.Request) {

        vars := mux.Vars(r)

        myString := vars["mystring"]


        w.WriteHeader(http.StatusOK)

        w.Header().Set("Content-Type", "text/plain")

        w.Write([]byte(myString))

}

这是对它的测试:


package main


import (

        "io/ioutil"

        "net/http"

        "testing"

        "time"


        "github.com/stretchr/testify/assert"

)


func TestGetRequest(t *testing.T) {

        go startServer()

        client := &http.Client{

                Timeout: 1 * time.Second,

        }


        r, _ := http.NewRequest("GET", "http://localhost:8080/test/abcd", nil)


        resp, err := client.Do(r)

        if err != nil {

                panic(err)

        }

        assert.Equal(t, http.StatusOK, resp.StatusCode)

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {

                panic(err)

        }

        assert.Equal(t, []byte("abcd"), body)

}

我认为这是一个更好的方法——你真的在测试你写的东西,因为它很容易在 go 中启动/停止侦听器!


查看完整回答
反对 回复 2021-11-29
  • 3 回答
  • 0 关注
  • 270 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信