Вот что я пытаюсь сделать:
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
в теле ответа.
unit test для функции 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
больше не существует.
Другим предлагаемым решением было бы изменить ваш код, чтобы функции запроса также принимали 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
не работает.