Ich habe einen tischgesteuerten Testfall wie diesen:
func CountWords(s string) map[string]int
func TestCountWords(t *testing.T) {
var tests = []struct {
input string
want map[string]int
}{
{"foo", map[string]int{"foo":1}},
{"foo bar foo", map[string]int{"foo":2,"bar":1}},
}
for i, c := range tests {
got := CountWords(c.input)
// TODO test whether c.want == got
}
}
Ich könnte überprüfen, ob die Längen gleich sind, und eine Schleife schreiben, die prüft, ob jedes Schlüssel-Wert-Paar gleich ist. Aber dann muss ich diesen Scheck noch einmal schreiben, wenn ich ihn für einen anderen Kartentyp verwenden möchte (sagen wir map[string]string
).
Am Ende habe ich die Maps in Strings konvertiert und die Strings verglichen:
func checkAsStrings(a,b interface{}) bool {
return fmt.Sprintf("%v", a) != fmt.Sprintf("%v", b)
}
//...
if checkAsStrings(got, c.want) {
t.Errorf("Case #%v: Wanted: %v, got: %v", i, c.want, got)
}
Dies setzt voraus, dass die Zeichenfolgendarstellungen äquivalenter Karten identisch sind, was in diesem Fall der Fall zu sein scheint (wenn die Schlüssel gleich sind, haben sie den gleichen Wert, sodass ihre Reihenfolge gleich ist). Gibt es einen besseren Weg, dies zu tun? Was ist die idiomatische Methode, um zwei Karten in tabellengesteuerten Tests zu vergleichen?
quelle
Antworten:
In der Go-Bibliothek sind Sie bereits vertreten. Mach das:
import "reflect" // m1 and m2 are the maps we want to compare eq := reflect.DeepEqual(m1, m2) if eq { fmt.Println("They're equal.") } else { fmt.Println("They're unequal.") }
Wenn Sie sich den Quellcode für
reflect.DeepEqual
denMap
Fall ansehen , werden Sie feststellen, dass zuerst überprüft wird, ob beide Karten Null sind, und dann überprüft wird, ob sie dieselbe Länge haben, bevor schließlich überprüft wird, ob sie denselben Satz von (Schlüssel, Wert) Paare.Da
reflect.DeepEqual
ein Schnittstellentyp verwendet wird, funktioniert er auf jeder gültigen Karte (map[string]bool, map[struct{}]interface{}
usw.). Beachten Sie, dass dies auch für Nicht-Kartenwerte funktioniert. Achten Sie also darauf, dass es sich bei der Übergabe tatsächlich um zwei Karten handelt. Wenn Sie zwei Ganzzahlen übergeben, wird Ihnen gerne mitgeteilt, ob sie gleich sind.quelle
c.Assert(m1, DeepEquals, m2)
. Das Schöne daran ist, dass der Test abgebrochen wird und Sie erfahren, was Sie erhalten haben und was Sie in der Ausgabe erwartet haben.Sie haben das Projekt
go-test/deep
zu helfen.Aber: Dies sollte mit Go 1.12 (Februar 2019) nativ einfacher sein : Siehe Versionshinweise .
Quellen:
golang/go
Ausgabe 21095 ,purpleidea
)Der CL fügt hinzu: ( CL steht für "Change List" )
Verwenden Sie auch das Paket in
text/template
, das bereits eine schwächere Version dieses Mechanismus hatte.Sie können sehen, dass in verwendet
src/fmt/print.go#printValue(): case reflect.Map:
quelle
fmt
Verhalten, die Gleichwertigkeit von Karten zu testen? Schlagen Sie vor, die Zeichenfolgendarstellungen zu vergleichen, anstatt sie zu verwendenDeepEqual
?DeepEqual
ist immer noch gut. (oder besser gesagtcmp.Equal
) Der Anwendungsfall wird in twitter.com/mikesample/status/1084223662167711744 näher erläutert , wie bei unterschiedlichen Protokollen, wie in der Originalausgabe angegeben: github.com/golang/go/issues/21095 . Das heißt: Abhängig von der Art Ihres Tests kann ein zuverlässiger Unterschied hilfreich sein.fmt.Sprint(map1) == fmt.Sprint(map2)
für die tl; drFolgendes würde ich tun (ungetesteter Code):
func eq(a, b map[string]int) bool { if len(a) != len(b) { return false } for k, v := range a { if w, ok := b[k]; !ok || v != w { return false } } return true }
quelle
map[string]float64
.eq
funktioniert nur fürmap[string]int
Karten. Sollte icheq
jedes Mal eine Version der Funktion implementieren, wenn ich Instanzen eines neuen Kartentyps vergleichen möchte?a
.Haftungsausschluss : Nicht im
map[string]int
Zusammenhang mit dem Testen der Äquivalenz von Karten in Go, dem Titel der FrageWenn Sie eine Karte eines Zeigertyp (wie haben
map[*string]int
), dann Sie haben nicht wollen reflect.DeepEqual verwenden , weil es falsch zurück.Wenn der Schlüssel ein Typ ist, der einen nicht exportierten Zeiger enthält, wie z. B. time.Time, reflektieren Sie. DepepEqual auf einer solchen Karte kann auch false zurückgeben .
quelle
Verwenden Sie die "Diff" -Methode von github.com/google/go-cmp/cmp :
Code:
// Let got be the hypothetical value obtained from some logic under test // and want be the expected golden data. got, want := MakeGatewayInfo() if diff := cmp.Diff(want, got); diff != "" { t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff) }
Ausgabe:
MakeGatewayInfo() mismatch (-want +got): cmp_test.Gateway{ SSID: "CoffeeShopWiFi", - IPAddress: s"192.168.0.2", + IPAddress: s"192.168.0.1", NetMask: net.IPMask{0xff, 0xff, 0x00, 0x00}, Clients: []cmp_test.Client{ ... // 2 identical elements {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"}, {Hostname: "espresso", IPAddress: s"192.168.0.121"}, { Hostname: "latte", - IPAddress: s"192.168.0.221", + IPAddress: s"192.168.0.219", LastSeen: s"2009-11-10 23:00:23 +0000 UTC", }, + { + Hostname: "americano", + IPAddress: s"192.168.0.188", + LastSeen: s"2009-11-10 23:03:05 +0000 UTC", + }, }, }
quelle
Einfachster Weg:
assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want)
Beispiel:
import ( "github.com/stretchr/testify/assert" "testing" ) func TestCountWords(t *testing.T) { got := CountWords("hola hola que tal") want := map[string]int{ "hola": 2, "que": 1, "tal": 1, } assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want) }
quelle
Verwenden Sie stattdessen cmp ( https://github.com/google/go-cmp ):
if !cmp.Equal(src, expectedSearchSource) { t.Errorf("Wrong object received, got=%s", cmp.Diff(expectedSearchSource, src)) }
Es schlägt immer noch fehl, wenn die "Reihenfolge" der Karte in Ihrer erwarteten Ausgabe nicht dem entspricht, was Ihre Funktion zurückgibt. Kann
cmp
jedoch immer noch darauf hinweisen, wo die Inkonsistenz liegt.Als Referenz habe ich diesen Tweet gefunden:
https://twitter.com/francesc/status/885630175668346880?lang=de
quelle
Eine der Optionen ist das Beheben von rng:
rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))
quelle