Unit-Tests für die Funktionen, die die Nutzung gorilla/mux: URL-Parameter
Hier ist, was ich versuche zu tun :
main.gehen
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))
}
Dies schafft eine grundlegende http-server lauscht auf port 8080
, dass Echos, die URL-parameter in den Weg. Also für http://localhost:8080/test/abcd
schreibt er wieder eine Antwort, die mit abcd
im response-body.
Den unit-test für die GetRequest()
Funktion ist in main_test.gehen :
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())
}
Das test-Ergebnis :
--- 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
Die Frage ist, wie kann ich fake die mux.Vars(r)
für die unit-tests?
Ich habe festgestellt, dass einige Diskussionen hier aber die vorgeschlagene Lösung funktioniert nicht mehr. Die vorgeschlagene Lösung war :
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
}
Diese Lösung funktioniert nicht, weil context.DefaultContext
und mux.ContextKey
nicht mehr existieren.
Anderen vorgeschlagenen Lösung wäre, ändern Sie Ihren code so, dass die Anfrage-Funktionen akzeptieren auch ein map[string]string
als Dritten parameter. Andere Lösungen sind eigentlich ab einem server und bauen Sie die Anfrage und senden Sie es direkt an den server. Meiner Meinung nach würde dies die Niederlage der Zweck von unit-Tests, die sich im wesentlichen in der funktionalen tests.
In Anbetracht der Tatsache das der verlinkte thread ist von 2013. Gibt es andere Optionen?
BEARBEITEN
Also ich habe gelesen, die gorilla/mux
source-code, und nach mux.go
die Funktion mux.Vars()
definiert ist hier wie diese :
//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
}
Den Wert varsKey
ist definiert als iota
hier. So im wesentlichen, wird der Wert des Schlüssels 0
. Ich habe eine schriftliche kleine test-app zu prüfen :
main.gehen
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
}
Welche, wenn Sie ausgeführt werden, Ausgänge :
Key: mystring Value: abcd
map[]
Was mich Wundern lässt, warum der test nicht funktioniert und warum der direkte Aufruf zu mux.Vars
funktioniert nicht.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Problem ist, auch wenn Sie
0
als Wert zu setzen, Kontext, Werte, ist es nicht Wert, dassmux.Vars()
liest.mux.Vars()
ist mitvarsKey
(wie Sie bereits gesehen haben), die von TypcontextKey
und nichtint
.Sicher,
contextKey
ist definiert als:was bedeutet, dass es int als zugrunde liegende Objekt, aber der Typ spielt das Teil beim Vergleich der Werte in gehen, so
int(0) != contextKey(0)
.Ich sehe nicht, wie man betrügen könnte, gorilla-mux oder Kontext in der Rückkehr Ihre Werte.
Dass gesagt wird, die paar Möglichkeiten, um dies zu testen in den Sinn kommt (beachten Sie, dass der nachfolgende code ist ungetestet, ich habe es direkt eingegeben hier, so könnte es einige dumme Fehler):
Anstatt server, nur mit gorilla-mux-Router in Ihren tests. In diesem Szenario, Sie hätten einen router, den Sie passieren, um
ListenAndServe
, aber das könnte man auch benutzen, mit demselben router-Instanz in tests und callServeHTTP
auf Sie. Router würde aufpassen Einstellung, Kontext, Werte, und Sie wäre in Ihrem Handler.irgendwo in der main-Funktion, die Sie tun würde, so etwas wie dieses:
und in den tests, die Sie tun können:
Wickeln Sie den Handler so, dass Sie akzeptieren URL-Parameter als drittes argument und wrapper aufrufen soll
mux.Vars()
- und übergeben, URL Parameter handler.Mit dieser Lösung, Ihre Handler hätte, Signatur:
und Sie würden anpassen müssen, um Anrufe zu entsprechen
http.Handler
Schnittstelle:- Handler zu registrieren, die Sie verwenden würden:
Welche Sie verwenden, ist Geschmackssache. Ich persönlich würde wahrscheinlich gehen mit option 2 oder 3, mit leichter Präferenz in Richtung 3.
gorilla/mux
sollte dieses Problem beheben, um unit-test möglich.vars
: github.com/gorilla/mux/blob/master/context_native_test.go#L22(type VarsHandler) as type func(http.ResponseWriter, *http.Request) in argument to router.HandleFunc
mux.SetURLVars()
gorilla/mux
bietet dieSetURLVars
- Funktion für Testzwecke, die Sie verwenden können, Spritzen Sie Ihre mockvars
.In golang, ich habe etwas anderen Ansatz zu testen.
Ich leicht umschreiben Ihre lib-code:
Und hier ist der test für ihn:
Ich denke, dies ist ein besserer Ansatz - Sie sind wirklich das testen, was Sie schrieb, da es sehr einfach zu starten/stoppen Zuhörer in der go!
gorilla/mux
die unit-tests funktionieren würde, und im Grunde könnte ich die Durchführung von tests, die parallel für jeden Endpunkt-handler-Funktion, ohne sich durch eine tatsächliche http-server. Wenn ich nur Funktions - /Abnahmetests würde ich vermeiden, das eigentliche problem, dassgorilla/mux
hat und meine tests würde länger dauern. Ich bin der Planung bis zur Nutzung sowohl funktionale und unit-tests in meinem Projekt.Verwende ich die folgende Hilfsfunktion zum aufrufen von Handler aus unit-tests:
Weil es keine (einfache) Möglichkeit zur Abmeldung von HTTP-Handlern und mehrere Aufrufe
http.Handle
für die gleiche Strecke wird scheitern. Daher ist die Funktion fügt eine neue route (z.B./1
oder/2
), damit der Pfad eindeutig ist. Diese Magie ist notwendig, um die Funktion in mehrere unit-test in den gleichen Prozess.Testen Sie Ihre
GetRequest
-Funktion:Das Problem ist Sie können nicht von vars.
Die Lösung ist die Schaffung eines neuen Routers für jeden test.
In Ihrer handler-Datei.
In Ihren tests.
Können Sie
*api.Route
wo einhttp.Handler
benötigt wird.Seit
context.setVar
ist nicht öffentlich aus Gorilla-Mux und noch nicht behoben dieses Problem in über 2 Jahren, habe ich beschlossen, ich würde nur machen einen workaround für mein server, der bekommt die variable aus einem header anstelle der Kontext, wenn die var leer ist. Da der var sollte nie leer sein, das ändert nichts an der Funktionalität meines Servers.Erstellen Sie eine Funktion, um mux.Vars
Dann statt
Einfach anrufen
Was bedeutet, dass Sie in Ihrer unit-tests können Sie eine Funktion hinzufügen
Dann stellen Sie Ihre Anfrage