У меня есть тестовый пример, управляемый таблицей, вроде этого:
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
}
}
Я мог бы проверить, одинаковы ли длины, и написать цикл, который проверяет, одинаковы ли все пары ключ-значение. Но потом мне придется снова написать эту проверку, когда я захочу использовать ее для другого типа карты (скажем map[string]string
).
В итоге я преобразовал карты в строки и сравнил строки:
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)
}
Это предполагает, что строковые представления эквивалентных карт одинаковы, что кажется правдой в этом случае (если ключи одинаковы, они хешируют одно и то же значение, поэтому их порядок будет одинаковым). Есть лучший способ сделать это? Каков идиоматический способ сравнения двух карт в табличных тестах?
testing
maps
go
equivalence
table-driven
Andras
источник
источник
Ответы:
Библиотека Go уже вас покрыла. Сделай это:
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.") }
Если вы посмотрите на исходный код для случая
reflect.DeepEqual
'Map
, вы увидите, что сначала он проверяет, равны ли обе карты нулю, затем проверяет, имеют ли они одинаковую длину, прежде чем, наконец, проверить, есть ли у них одинаковый набор (ключ, значение) пары.Поскольку
reflect.DeepEqual
принимает тип интерфейса, он будет работать на любой действующей карте (map[string]bool, map[struct{}]interface{}
и т. Д.). Обратите внимание, что он также будет работать со значениями, не относящимися к карте, поэтому будьте осторожны, потому что на самом деле вы передаете ему две карты. Если вы передадите ему два целых числа, он с радостью скажет вам, равны ли они.источник
c.Assert(m1, DeepEquals, m2)
. Что хорошо в этом, так это то, что он прерывает тест и сообщает вам, что вы получили и чего ожидали в результате.У вас есть проект,
go-test/deep
чтобы помочь.Но: это должно быть проще с Go 1.12 (февраль 2019 г.) изначально : см. Примечания к выпуску .
Источники:
golang/go
выпуск 21095 ,purpleidea
)CL добавляет: ( CL означает «Список изменений» )
Также используйте пакет, в
text/template
котором уже была более слабая версия этого механизма.Вы можете видеть, что используется в
src/fmt/print.go#printValue(): case reflect.Map:
источник
fmt
поведение помогает проверить эквивалентность карт? Вы предлагаете сравнить строковые представления вместо использованияDeepEqual
?DeepEqual
все еще хорош. (или, скорееcmp.Equal
) вариант использования более проиллюстрирован в twitter.com/mikesample/status/1084223662167711744 , например, журналы сравнения, как указано в исходном выпуске: github.com/golang/go/issues/21095 . Это означает: в зависимости от характера вашего теста может помочь надежный diff.fmt.Sprint(map1) == fmt.Sprint(map2)
для tl; drВот что я бы сделал (непроверенный код):
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 }
источник
map[string]float64
.eq
работает только дляmap[string]int
карт. Должен ли я реализовывать версиюeq
функции каждый раз, когда я хочу сравнить экземпляры нового типа карты?a
.Отказ от ответственности : не
map[string]int
связано, но связано с тестированием эквивалентности карт в Go, что является заголовком вопросаЕсли у вас есть карта типа указателя (например
map[*string]int
), то вы ничего не хотите использовать reflect.DeepEqual , потому что он возвращает ложь.Наконец, если ключ является типом, содержащим неэкспортированный указатель, например time.Time, то рефлексия.DeepEqual на такой карте также может возвращать false .
источник
Используйте метод "Diff" на github.com/google/go-cmp/cmp :
Код:
// 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) }
Выход:
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", + }, }, }
источник
Самый простой способ:
assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want)
Пример:
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) }
источник
Вместо этого используйте cmp ( https://github.com/google/go-cmp ):
if !cmp.Equal(src, expectedSearchSource) { t.Errorf("Wrong object received, got=%s", cmp.Diff(expectedSearchSource, src)) }
Он все равно терпит неудачу, если "порядок" карты в ожидаемом выводе не соответствует тому, что возвращает ваша функция. Тем не менее,
cmp
он все же может указать, в чем заключается несоответствие.Для справки я нашел этот твит:
https://twitter.com/francesc/status/885630175668346880?lang=en
источник
Один из вариантов - исправить rng:
rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))
источник