TLDR:gorilla/mux过去不提供设置URL变量的功能,现在有了,这就是为什么第二多的支持率的答案在很长一段时间内都是正确的答案。
最初的问题如下:
这就是我要做的:
"主星,开始"
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的讨论,但是提议的解决方案不再起作用。提议的解决方案是:
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
和mux.ContextKey
不再存在,因此此解决方案不起作用。
另一个建议的解决方案是修改代码,使请求函数也接受map[string]string
作为第三个参数。其他的解决方案包括实际启动一个服务器,构建请求并将其直接发送到服务器。在我看来,这将破坏单元测试的目的,将其本质上变成功能测试。
考虑到链接的线程是2013年的,还有其他选择吗?
编辑
我看过gorilla/mux
的源代码,根据mux.go
,函数mux.Vars()
的定义如下:
// 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
。所以本质上,键值是0
。我写了一个小的测试应用程序来检查这一点:"主星,开始"
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
不起作用。
5条答案
按热度按时间osh3o9ms1#
gorilla/mux
提供了用于测试的SetURLVars
函数,您可以使用该函数注入mockvars
。o2rvlv0m2#
问题是,即使您使用
0
作为值来设置上下文值,它也不是mux.Vars()
读取的值。mux.Vars()
使用的是varsKey
(正如您已经看到的),它的类型是contextKey
,而不是int
。确定,
contextKey
定义为:这意味着它的底层对象是int,但是在Go语言中比较值时,type起作用,所以
int(0) != contextKey(0)
。我看不出你如何能欺骗gorillamux或context返回你的值。
话虽如此,我想到了几种测试方法(注意下面的代码未经测试,我直接在这里输入,所以可能会有一些愚蠢的错误):
1.正如有人建议的那样,运行一个服务器并向其发送HTTP请求。
1.在你的测试中使用gorilla mux Router而不是运行服务器。在这个场景中,你会有一个路由器,你传递给
ListenAndServe
,但是你也可以在测试中使用同一个路由器示例,并在它上面调用ServeHTTP
。Router将负责设置上下文值,它们将在你的处理程序中可用。在main函数中,您可以执行如下操作:
在测试中,您可以:
mux.Vars()
并将URL参数传递给处理程序使用此解决方案,您的处理程序将具有签名:
并且您必须调整对它的调用以符合
http.Handler
接口:若要注册行程常式,请用途:
你用哪一个是个人喜好的问题。就我个人而言,我可能会选择2或3,略倾向于3。
vzgqcmou3#
在golang,我有稍微不同的测试方法。
我稍微重写了您的lib代码:
下面是对它的测试:
我认为这是一个更好的方法-你真的是在测试你写的东西,因为它非常容易在go中启动/停止监听器!
pepwfjgg4#
我使用下面的helper函数从单元测试中调用处理程序:
因为没有简单的方法来取消注册HTTP句柄,并且对同一个路由的
http.Handle
的多次调用将会失败。因此,该函数添加了一个新的路由(例如/1
或/2
)来确保路径是唯一的。在同一个进程中的多个单元测试中使用该函数时,这种魔法是必要的。要测试
GetRequest
-函数:zyfwsgd65#
问题是你不能设置变量。
解决方案是在每次测试中创建一个新路由器。
在你的处理程序文件里。
在你的测试中。
您可以在需要
http.Handler
的任何地方使用*api.Route
。