webentwicklung-frage-antwort-db.com.de

Wie kann man die Gleichwertigkeit von Karten in Golang testen?

Ich habe einen tabellengetriebenen 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 prü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 Sie 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 Zuordnungen identisch sind, was in diesem Fall zutreffend zu sein scheint (wenn die Schlüssel identisch sind, haben sie denselben Wert, sodass ihre Reihenfolge identisch ist). Gibt es einen besseren Weg, dies zu tun? Wie lassen sich zwei Karten in tabellengesteuerten Tests idiomatisch vergleichen?

65
andras

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 den Fall reflect.DeepEqualMap ansehen, werden Sie feststellen, dass zuerst geprüft wird, ob beide Maps null sind, und dann, ob sie leer sind haben die gleiche Länge, bevor sie schließlich überprüfen, ob sie die gleiche Menge von (Schlüssel-, Wert-) Paaren haben.

Da reflect.DeepEqual Einen Schnittstellentyp annimmt, funktioniert dies auf jeder gültigen Karte (map[string]bool, map[struct{}]interface{} Usw.). Beachten Sie, dass es auch für Nicht-Kartenwerte funktioniert. Achten Sie also darauf, dass es sich bei der Übergabe um zwei Karten handelt. Wenn Sie zwei Ganzzahlen übergeben, wird Ihnen gerne mitgeteilt, ob sie gleich sind.

127
joshlf

Das 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
}
10
zzzz

Wie lassen sich zwei Karten in tabellengesteuerten Tests idiomatisch vergleichen?

Sie haben das Projekt go-test/deep zu helfen.

Aber: dies sollte einfacher sein mit Go 1.12 (Februar 2019) nativ: Siehe Versionshinweise .

fmt

Die Karten werden jetzt in einer nach Schlüsseln sortierten Reihenfolge gedruckt, um das Testen zu vereinfachen .

Die Bestellregeln sind:

  • Wenn zutreffend, vergleicht Null niedrig
  • ints, Floats und Strings sortiert nach <
  • NaN vergleicht weniger als Nicht-NaN-Floats
  • bool vergleicht false vor true
  • Komplex vergleicht real, dann imaginär
  • Zeiger vergleichen nach Maschinenadresse
  • Kanalwerte werden nach Maschinenadresse verglichen
  • Strukturen vergleichen nacheinander jedes Feld
  • Arrays vergleichen nacheinander jedes Element
  • Die Schnittstellenwerte werden zuerst mit reflect.Type Verglichen, das den konkreten Typ beschreibt, und dann mit dem konkreten Wert, wie in den vorherigen Regeln beschrieben.

Beim Drucken von Karten wurden nicht reflexive Schlüsselwerte wie NaN zuvor als angezeigt. Ab dieser Version werden die korrekten Werte gedruckt.

Quellen:

Der CL fügt hinzu: ( CL steht für "Change List" )

Dazu fügen wir ein Paket im Stammverzeichnis, internal/fmtsort hinzu, das einen allgemeinen Mechanismus zum Sortieren von Zuordnungsschlüsseln unabhängig von ihrem Typ implementiert.

Dies ist ein wenig chaotisch und wahrscheinlich langsam, aber das formatierte Drucken von Karten war noch nie so schnell und ist bereits immer reflexionsgesteuert.

Das neue Paket ist intern, weil wir wirklich nicht wollen, dass jeder das benutzt, um Dinge zu sortieren. Es ist langsam, nicht allgemein und nur für die Untergruppe von Typen geeignet, bei denen es sich um Map-Keys handeln kann.

Verwenden Sie auch das Paket in text/template, Das bereits eine schwächere Version dieses Mechanismus hatte.

Sie können sehen, dass in src/fmt/print.go# verwendet

8
VonC

Haftungsausschluss : Nicht verbunden mit map[string]int aber im Zusammenhang mit der Prüfung der Gleichwertigkeit von Karten in Go, das ist der Titel der Frage

Wenn Sie eine Karte eines Zeigertyps haben (wie map[*string]int), dann möchten Sie nicht reflect.DeepEqual verwenden, da es false zurückgibt.

Wenn der Schlüssel ein Typ ist, der einen nicht exportierten Zeiger enthält, z. B. time.Time, reflektieren Sie schließlich.DeepEqual auf einer solchen Map kann auch false zurückgeben .

4
Carl

Inspiriert von diese Antwort , benutze ich Folgendes, um die Gleichwertigkeit von Karten in Golang zu testen:

import (
    "reflect"
)

func TestContinuationTokenHash(t *testing.T) {
    expected := []string{"35303a6235633862633138616131326331613030356565393061336664653966613733",
        "3130303a6235633862633138616131326331613030356565393061336664653966613733",
        "3135303a6235633862633138616131326331613030356565393061336664653966613733",
        "null"}
    actual := continuationTokenRecursion("null")
    if !reflect.DeepEqual(expected, actual) {
        t.Errorf("Maps not equal. Expected %s, but was %s.", expected, actual)
    }
}

https://github.com/030/nexus3-cli

0
030

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",
+       },
    },
  }
0
Jonas Felber