这是我想做的事情:
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)
为单元测试伪造?我在这里找到了一些讨论,但是建议的解决方案不再起作用。提议的解决方案是:
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
不再存在。
另一个建议的解决方案是更改您的代码,以便请求函数也接受a 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
。我编写了一个小型测试应用程序来检查此问题: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
不起作用。
问题是,即使当您将其0
用作值来设置上下文值时,它也不会mux.Vars()
读取相同的值。mux.Vars()
正在使用varsKey
(如您所见)的类型contextKey
而不是int
。
当然,contextKey
定义为:
type contextKey int
这意味着它具有int作为基础对象,但在比较go中的值时,type起作用int(0) != contextKey(0)
。
我看不到如何欺骗大猩猩多态或上下文返回您的值。
话虽这么说,想到了几种测试方法(请注意,以下代码未经测试,我在这里直接输入了代码,因此可能存在一些愚蠢的错误):
无需运行服务器,只需在测试中使用gorilla mux路由器即可。在这种情况下,您将拥有一个传递给的路由器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略有偏爱。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句