2013-08-13 8 views
36

Ich habe einen tabellengesteuerte Testfall wie diesen:Testing Gleichwertigkeit von Karten (Golang)

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 
    } 
} 

konnte ich überprüfen, ob die Längen gleich sind und eine Schleife schreiben, wenn jeder Schlüssel-Wert-Paar überprüft ist dasselbe. Aber dann muss ich diese Prüfung erneut schreiben, wenn ich sie für eine andere Art von Karte verwenden möchte (zB map[string]string).

Was ich tat endete, umgerechnet ich die Karten in Strings und verglichen die Saiten:

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 Stringdarstellungen äquivalenter Karten gleich sind, die in diesem Fall um wahr zu sein scheinen (Wenn die Schlüssel identisch sind, werden sie auf den gleichen Wert gerastert, so dass ihre Aufträge gleich sind). Gibt es einen besseren Weg, dies zu tun? Was ist der idiomatische Weg, zwei Maps in tabellengesteuerten Tests zu vergleichen? Diese

+3

Err, nein: Die Reihenfolge Iterieren eine Karte nicht sein [berechenbar] gewährleistet ist (http://golang.org/ref/spec#For_statements): _ "Die Iterationsreihenfolge über Maps ist nicht spezifiziert und es ist nicht garantiert, dass sie von einer Iteration zur nächsten identisch sind. ..." _. – zzzz

+2

Darüber hinaus wird Go für Karten bestimmter Größen die Reihenfolge absichtlich zufällig verteilen. Es ist sehr ratsam, sich nicht auf diese Reihenfolge zu verlassen. –

Antwort

77

Die Go-Bibliothek bereits hat für Sie. Tun Sie dies:

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 man sich die source code für reflect.DeepEqual ‚s Map Fall schauen, sehen Sie, dass es zuerst überprüft, ob die beiden Karten gleich Null sind, dann überprüft er, ob sie die gleiche Länge haben, bevor sie schließlich zu überprüfen, sehen Sie, ob sie den gleichen Satz von (Schlüssel, Wert) Paaren haben.

Da reflect.DeepEqual einen Schnittstellentyp verwendet, funktioniert er auf jeder gültigen Karte (map[string]bool, map[struct{}]interface{} usw.). Beachten Sie, dass dies auch bei Nicht-Map-Werten funktioniert. Achten Sie also darauf, dass es sich bei dem, was Sie übergeben, um zwei Maps handelt. Wenn Sie zwei Ganzzahlen übergeben, wird es Ihnen gerne sagen, ob sie gleich sind.

+0

Großartig, genau das habe ich gesucht. Ich nehme an, wie jnml sagte, es ist nicht so performant, aber wen interessiert das in einem Testfall. – andras

+0

Ja, wenn Sie das jemals für eine Produktionsanwendung wollen, würde ich definitiv mit einer benutzerdefinierten geschriebene Funktion gehen, wenn möglich, aber dies tut definitiv den Trick, wenn Leistung keine Rolle spielt. – joshlf

+1

@andras Sie sollten auch [gocheck] (http://labix.org/gocheck) auschecken. So einfach wie 'c.Assert (m1, DeepEquals, m2)'. Was ist schön daran ist, dass es den Test abbricht und Ihnen sagt, was Sie bekommen haben und was Sie in der Ausgabe erwartet haben. – Luke

6

ist das, was ich würde (ungetestet Code) tun:

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 
} 
+0

OK, aber ich habe einen anderen Testfall, wo ich Instanzen von 'map [string] float64 'vergleichen möchte. 'eq' funktioniert nur für' map [string] int'-Maps. Sollte ich jedes Mal eine Version der 'eq'-Funktion implementieren, wenn ich Instanzen eines neuen Kartentyps vergleichen möchte? – andras

+0

@andras: 11 SLOCs. Ich würde "Paste kopieren" es in kürzerer Zeit spezialisieren, als es braucht, um darüber zu fragen. Obwohl viele andere "reflect" verwenden würden, um dasselbe zu tun, aber das ist von viel schlechterer Leistung. – zzzz

+1

erwartet nicht, dass die Karten in der gleichen Reihenfolge sind? Was nicht garantiert ist, siehe "Iterationsreihenfolge" auf https: //blog.golang.org/go-maps-in-action – nathj07

1

Haftungsausschluss: Ohne Bezug zu map[string]int aber bezogen auf die Gleichwertigkeit von Karten in Go Prüfung, die der Titel der Frage ist

Wenn Sie eine Karte eines Zeigertyp (wie map[*string]int) haben, dann sind Sie do not want to use reflect.DeepEqual weil es falsch zurückgibt. Wenn der Schlüssel ein Typ ist, der einen nicht exportierten Zeiger enthält, wie z. B. time.Time, dann reflect.DeepEqual auf einer solchen Karte. can also return false.

-2

Eine der Optionen ist zu beheben vMb:

rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF)) 
+0

kümmern, um den Downvote zu erklären? – Grozz

Verwandte Themen