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 compruebe si todos los pares clave-valor son iguales. Pero luego tengo que escribir este cheque nuevamente cuando quiera 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 supone que las representaciones de cadenas de mapas equivalentes son las mismas, lo que parece ser cierto en este caso (si las claves son las mismas, entonces obtienen el 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?
testing
maps
go
equivalence
table-driven
andras
fuente
fuente
Respuestas:
La biblioteca Go ya lo tiene cubierto. Hacer 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 observa el código fuente del caso de
reflect.DeepEqual
'Map
, verá que primero verifica si ambos mapas son nulos, luego verifica si tienen la misma longitud antes de verificar finalmente si tienen el mismo conjunto de (clave, valor) pares.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á con valores que no sean mapas, así que tenga cuidado de que lo que le está pasando sean realmente dos mapas. Si le pasa dos enteros, felizmente le dirá si son iguales.fuente
c.Assert(m1, DeepEquals, m2)
. Lo bueno de esto es que aborta la prueba y le dice lo que obtuvo y lo que esperaba en la salida.Tienes el proyecto
go-test/deep
para ayudarte.Pero: esto debería ser más fácil con Go 1.12 (febrero de 2019) de forma nativa : consulte las notas de la versión .
Fuentes:
golang/go
número 21095 ,purpleidea
)La CL agrega: ( CL significa "Lista de cambios" )
También use el paquete en
text/template
, que ya tenía una versión más débil de este mecanismo.Puedes ver que se usa en
src/fmt/print.go#printValue(): case reflect.Map:
fuente
fmt
ayuda exactamente este nuevo comportamiento a probar la equivalencia de los mapas? ¿Sugiere comparar las representaciones de cadenas en lugar de utilizarlasDeepEqual
?DeepEqual
sigue siendo bueno. (o más biencmp.Equal
) El caso de uso está más ilustrado en twitter.com/mikesample/status/1084223662167711744 , como registros diferentes como se indica en el número original: github.com/golang/go/issues/21095 . Significado: dependiendo de la naturaleza de su prueba, una diferencia confiable puede ayudar.fmt.Sprint(map1) == fmt.Sprint(map2)
para el tl; drEsto 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 }
fuente
map[string]float64
.eq
solo funciona paramap[string]int
mapas. ¿Debo implementar una versión de laeq
función cada vez que quiera comparar instancias de un nuevo tipo de mapa?a
.Descargo de responsabilidad : no
map[string]int
relacionado pero relacionado con probar la equivalencia de mapas en Go, que es el título de la preguntaSi usted tiene un mapa de un tipo de puntero (como
map[*string]int
), entonces qué no querer usar reflect.DeepEqual porque va a devolver false.Finalmente, si la clave es un tipo que contiene un puntero no exportado, como time.Time, reflect.DeepEqual en dicho mapa también puede devolver falso .
fuente
Utilice el método "Diff" de github.com/google/go-cmp/cmp :
Código:
// 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) }
Salida:
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", + }, }, }
fuente
Manera más sencilla:
assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want)
Ejemplo:
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) }
fuente
Utilice cmp ( https://github.com/google/go-cmp ) en su lugar:
if !cmp.Equal(src, expectedSearchSource) { t.Errorf("Wrong object received, got=%s", cmp.Diff(expectedSearchSource, src)) }
Todavía falla cuando el "orden" del mapa en su salida esperada no es lo que devuelve su función. Sin embargo,
cmp
todavía puede señalar dónde está la inconsistencia.Como referencia, encontré este tweet:
https://twitter.com/francesc/status/885630175668346880?lang=es
fuente
Una de las opciones es arreglar rng:
rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))
fuente