Testing equivalence of maps (Golang)


Tengo un caso de prueba basado en tablas como este:

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

Podría comprobar si las longitudes son las mismas y escribir un bucle que comprueba si cada par clave-valor es el mismo. Pero luego tengo que escribir esta comprobación de nuevo cuando quiero usarlo para otro tipo de mapa (digamos map[string]string).

Lo que terminé haciendo es convertir los mapas en cadenas y comparar las cadenas:

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

Esto asume que las representaciones de cadena de los mapas equivalentes son las mismas, lo que parece ser true en este caso (si las claves son las mismas entonces hash al mismo valor, por lo que sus órdenes serán las mismas). ¿Hay una mejor manera de hacer esto? ¿Cuál es la forma idiomática de comparar dos mapas en pruebas basadas en tablas?

Author: Noob Coder, 2013-08-13

4 answers

La biblioteca Go ya te tiene cubierto. Haga esto:

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.")
}

Si nos fijamos en el código fuente para el caso reflect.DeepEqual Map, verá que primero comprueba si ambos mapas son nil, luego comprueba si tienen la misma longitud antes de comprobar finalmente si tienen el mismo conjunto de pares (clave, valor).

Debido a que reflect.DeepEqual toma un tipo de interfaz, funcionará en cualquier mapa válido (map[string]bool, map[struct{}]interface{}, etc). Tenga en cuenta que también funcionará en valores no mapeados, así que tenga cuidado de que lo que pasar a ella son realmente dos mapas. Si le pasa dos enteros, felizmente le dirá si son iguales.

 96
Author: joshlf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-26 21:20:13

Esto es lo que haría (código no probado):

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
}
 7
Author: zzzz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-13 11:59:18

Disclaimer : No relacionado con map[string]int pero relacionado con probar la equivalencia de los mapas en Go, que es el título de la pregunta

Si tiene un mapa de tipo puntero (como map[*string]int), entonces no quiere usar reflect.DeepEqual porque devolverá false.

Finalmente, si la clave es un tipo que contiene un puntero no exportado, como time.Tiempo, luego reflexionar.DeepEqual en tal mapa también puede devolver false.

 1
Author: Carl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-28 21:34:53

Una de las opciones es arreglar rng:

rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))
 -2
Author: Grozz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-21 17:41:59