Подтвердить что ты не робот

Как проверить эквивалентность карт в Голанге?

У меня есть настольный тестовый пример, подобный этому:

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 для одного и того же значения, поэтому их заказы будут одинаковыми). Есть лучший способ сделать это? Каков идиоматический способ сравнения двух карт в табличных тестах?

4b9b3361

Ответ 1

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

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

Ответ 2

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

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
}

Ответ 3

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

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

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

Ответ 4

Какой идиоматический способ сравнить две карты в тестах на основе таблиц?

У вас есть проект go-test/deep чтобы помочь.

Но: это должно быть проще с Go 1.12 (февраль 2019) изначально: см. Примечания к выпуску.

fmt

Карты теперь печатаются в порядке сортировки ключей, чтобы упростить тестирование.

Правила заказа:

  • Когда это применимо, ноль сравнивается с низким
  • порядок чисел, чисел с плавающей точкой и строк по <
  • NaN сравнивает меньше, чем не-NaN
  • bool сравнивает false перед true
  • Комплекс сравнивает реальное, а затем и воображаемое
  • Указатели сравниваются по машинному адресу
  • Значения каналов сравниваются по машинному адресу
  • Структуры сравнивают каждое поле по очереди
  • Массивы сравнивают каждый элемент по очереди
  • Значения интерфейса сначала сравниваются по reflect.Type описывающий конкретный тип, а затем по конкретному значению, как описано в предыдущих правилах.

При печати карт значения неотражающих ключей, такие как NaN, ранее отображались как. Начиная с этого выпуска, правильные значения печатаются.

Источники:

CL добавляет: (CL означает "Список изменений")

Для этого мы добавляем в корень пакет internal/fmtsort, который реализует общий механизм сортировки ключей карты независимо от их типа.

Это немного грязно и, вероятно, медленно, но форматированная печать карт никогда не была быстрой и уже всегда основывалась на отражении.

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

Также используйте пакет text/template, который уже имел более слабую версию этого механизма.

Вы можете видеть, что используется в src/fmt/print.go#

Ответ 5

Вдохновленный этим ответом, я использую следующее, чтобы проверить эквивалентность карт в Голанге:

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

Ответ 6

Используйте метод "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",
+       },
    },
  }

Ответ 7

Один из вариантов - исправить rng:

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