TLDR:gorilla/mux 过去不提供设置 URL 变量的可能性。现在确实如此,这就是为什么第二高得票数的答案在很长一段时间内都是正确的答案。
原始问题如下:
这就是我想做的:
main.go
package main
import (
"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 main
import (
"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>
FAIL
FAIL command-line-arguments 0.045s
问题是我如何伪造mux.Vars(r)
用于单元测试?
我发现了一些讨论here https://groups.google.com/d/topic/gorilla-web/TKOE2I0116Y但所提出的解决方案不再有效。建议的解决方案是:
func buildRequest(method string, url string, doctype uint32, docid uint32) *http.Request {
req, _ := http.NewRequest(method, url, nil)
req.ParseForm()
var vars = map[string]string{
"doctype": strconv.FormatUint(uint64(doctype), 10),
"docid": strconv.FormatUint(uint64(docid), 10),
}
context.DefaultContext.Set(req, mux.ContextKey(0), vars) // mux.ContextKey exported
return req
}
此解决方案不起作用,因为context.DefaultContext
and mux.ContextKey
不复存在。
另一个建议的解决方案是更改您的代码,以便请求函数也接受map[string]string
作为第三个参数。其他解决方案包括实际启动服务器并构建请求并将其直接发送到服务器。在我看来,这将违背单元测试的目的,将它们本质上变成功能测试。
考虑到链接的线程是 2013 年的事实。还有其他选择吗?
EDIT
所以我读过gorilla/mux
源代码,并根据mux.go
功能mux.Vars()
被定义为here https://github.com/gorilla/mux/blob/master/mux.go#L306像这样 :
// Vars returns the route variables for the current request, if any.
func Vars(r *http.Request) map[string]string {
if rv := context.Get(r, varsKey); rv != nil {
return rv.(map[string]string)
}
return nil
}
的价值varsKey
定义为iota
here https://github.com/gorilla/mux/blob/master/mux.go#L302。所以本质上,关键值是0
。我编写了一个小型测试应用程序来检查这一点:main.go
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/context"
)
func main() {
r, _ := http.NewRequest("GET", "/test/abcd", nil)
vars := map[string]string{
"mystring": "abcd",
}
context.Set(r, 0, vars)
what := Vars(r)
for key, value := range what {
fmt.Println("Key:", key, "Value:", value)
}
what2 := mux.Vars(r)
fmt.Println(what2)
for key, value := range what2 {
fmt.Println("Key:", key, "Value:", value)
}
}
func Vars(r *http.Request) map[string]string {
if rv := context.Get(r, 0); rv != nil {
return rv.(map[string]string)
}
return nil
}
运行时输出:
Key: mystring Value: abcd
map[]
这让我想知道为什么测试不起作用以及为什么直接调用mux.Vars
不起作用。