Seb*_*iuc 23 unit-testing go gorilla testify
这是我正在尝试做的事情:
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))
}
Run Code Online (Sandbox Code Playgroud)
这将创建一个基于端口的基本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())
}
Run Code Online (Sandbox Code Playgroud)
测试结果如下:
--- 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
Run Code Online (Sandbox Code Playgroud)
问题是如何伪造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
}
Run Code Online (Sandbox Code Playgroud)
此解决方案不起作用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
}
Run Code Online (Sandbox Code Playgroud)
值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
}
Run Code Online (Sandbox Code Playgroud)
运行时,输出:
Key: mystring Value: abcd
map[]
Run Code Online (Sandbox Code Playgroud)
这让我想知道为什么测试不起作用以及为什么直接调用mux.Vars不起作用.
del*_*boy 20
麻烦的是,即使您使用0值作为设置上下文值,它也不是mux.Vars()读取的值.mux.Vars()正在使用varsKey(如你所见)哪种类型contextKey而不是int.
当然,contextKey定义为:
type contextKey int
Run Code Online (Sandbox Code Playgroud)
这意味着它有int作为底层对象,但是在比较go中的值时类型会起作用,所以int(0) != contextKey(0).
我不知道如何欺骗大猩猩多路复用器或上下文来返回你的价值观.
话虽如此,我想到了几种测试方法(注意下面的代码是未经测试的,我在这里直接输入,所以可能会有一些愚蠢的错误):
而不是运行服务器,只需在测试中使用gorilla mux Router.在这种情况下,您将传递一个路由器ListenAndServe,但您也可以在测试中使用相同的路由器实例并对其进行调用ServeHTTP.路由器将负责设置上下文值,它们将在您的处理程序中可用.
func Router() *mux.Router {
r := mux.Router()
r.HandleFunc("/employees/{1}", GetRequest)
(...)
return r
}
Run Code Online (Sandbox Code Playgroud)
在main函数的某个地方,你会做这样的事情:
http.Handle("/", Router())
Run Code Online (Sandbox Code Playgroud)
在你的测试中你可以做到:
func TestGetRequest(t *testing.T) {
r := http.NewRequest("GET", "employees/1", nil)
w := httptest.NewRecorder()
Router().ServeHTTP(w, r)
// assertions
}
Run Code Online (Sandbox Code Playgroud)包装处理程序,以便它们接受URL参数作为第三个参数,包装器应调用mux.Vars()并将URL参数传递给处理程序.
使用此解决方案,您的处理程序将具有签名:
type VarsHandler func (w http.ResponseWriter, r *http.Request, vars map[string]string)
Run Code Online (Sandbox Code Playgroud)
你必须调整它的调用以符合http.Handler接口:
func (vh VarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
vh(w, r, vars)
}
Run Code Online (Sandbox Code Playgroud)
要注册处理程序,您将使用:
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")
Run Code Online (Sandbox Code Playgroud)你使用哪一个是个人喜好的问题.就个人而言,我可能选择2或3,略微偏向3.
小智 14
您需要将测试更改为:
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())
}
Run Code Online (Sandbox Code Playgroud)