2013-08-13 3 views
36

У меня есть таблица с приводом тестовый пример, как этот:Тестирование эквивалентности карт (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 
    } 
} 

Я мог бы проверить длины одинаковы и написать цикл, который проверяет, является ли каждая пара ключ-значение та же. Но тогда я должен снова написать эту проверку, когда захочу использовать ее для другого типа карты (скажем 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) 
} 

Это предполагает, что строковые представления эквивалентных отображений являются одинаковыми, что, кажется, верно в этом случае (если ключи одинаковы, то они имеют значение hh с тем же значением, поэтому их заказы будут одинаковыми). Есть лучший способ сделать это? Каков идиоматический способ сравнения двух карт в табличных тестах?

+3

Err, нет: Порядок итерация карта не гарантируется [предсказуемый] (http://golang.org/ref/spec#For_statements): _ «Порядок итераций по картам не указан и не может быть одинаковым с одной итерации на следующую. ...» _. – zzzz

+2

Кроме того, для карт определенных размеров Go будет преднамеренно ранжировать заказ. Очень важно не зависеть от этого порядка. –

ответ

77

Библиотека 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.") 
} 

Если посмотреть на source code для reflect.DeepEqualMap случае, вы увидите, что он сначала проверяет, если обе карты равны нулю, то он проверяет, если они имеют одинаковую длину, прежде чем, наконец, проверяя посмотрите, есть ли у них одинаковый набор пар (ключ, значение).

Потому что reflect.DeepEqual берет тип интерфейса, он будет работать с любой допустимой картой (map[string]bool, map[struct{}]interface{} и т. Д.). Обратите внимание, что он также будет работать с не-картографическими значениями, поэтому будьте осторожны, что то, что вы передаете ему, действительно является двумя картами. Если вы передадите ему два целых числа, он с радостью скажет вам, равны ли они.

+0

Удивительный, это именно то, что я искал. Я предполагаю, что jnml говорил, что это не так сильно, но кто интересуется тестовым случаем. – andras

+0

Да, если вы когда-либо захотите этого для производственного приложения, я бы, безусловно, поступил с помощью специально написанной функции, но это определенно делает трюк, если производительность не вызывает беспокойства. – joshlf

+1

@andras Вы также должны проверить [gocheck] (http://labix.org/gocheck). Так же просто, как 'c.Assert (m1, DeepEquals, m2)'. Что приятно в этом, так это прерывание теста и расскажет вам, что вы получили, и то, что вы ожидали в выходе. – Luke

6

Это то, что я хотел бы сделать (непроверенный код):

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

ОК, но у меня есть другой тестовый пример, где я хочу сравнить экземпляры 'map [string] float64'. 'eq' работает только для карт map [string] int'. Должен ли я реализовывать версию функции «eq» каждый раз, когда я хочу сравнивать экземпляры нового типа карты? – andras

+0

@andras: 11 SLOCs. Я бы «скопировал пасту», специализируясь на ней быстрее, чем нужно, чтобы спросить об этом. Хотя, многие другие будут использовать «отражать», чтобы сделать то же самое, но это намного хуже. – zzzz

+1

не означает, что карты будут в том же порядке? Который не гарантирует, что см. «Порядок итераций» на https: //blog.golang.org/go-maps-in-action – nathj07

1

Отказ от ответственности: Unrelated к map[string]int, но относящийся к проверке эквивалентности карт в Go, которая является названием вопроса

Если у вас есть карта типа указателя (например, map[*string]int), то вы do not want to use reflect.DeepEqual потому что он вернет false.

И, наконец, если ключ является типом, который содержит нераскрытый указатель, например time.Time, затем отражает.DeepEqual на такой карте can also return false.

-2

Один из вариантов, чтобы исправить RNG:

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

Помогите объяснить нижний план? – Grozz

Смежные вопросы