Nice programing

Golang에서지도의 동등성을 테스트하는 방법은 무엇입니까?

nicepro 2020. 11. 10. 22:15
반응형

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

이것은 동등한 맵의 문자열 표현이 동일하다고 가정합니다.이 경우에는 사실 인 것처럼 보입니다 (키가 동일하면 동일한 값으로 해시되므로 순서가 동일합니다). 이 작업을 수행하는 더 좋은 방법이 있습니까? 테이블 기반 테스트에서 두 맵을 비교하는 관용적 방법은 무엇입니까?


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.DeepEqualMap경우 그들이 전에 마지막으로 그들이 (키의 동일한 세트가 있는지 확인 같은 길이가 있다면, 당신은 다음 수표, 모두 매핑하고있는 경우에 최초로 확인이 전무 것을 볼 수 있습니다 값) 쌍.

reflect.DeepEqual인터페이스 유형을 취하기 때문에 유효한 모든지도 ( map[string]bool, map[struct{}]interface{}등)에서 작동합니다. 비맵 값에서도 작동하므로 전달하는 것이 실제로 두 개의 맵이라는 점에 유의하십시오. 두 개의 정수를 전달하면 같은지 여부를 기꺼이 알려줍니다.


이것은 내가 할 일입니다 (테스트되지 않은 코드).

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
}

테이블 기반 테스트에서 두 맵을 비교하는 관용적 방법은 무엇입니까?

당신은 go-test/deep도울 프로젝트 가 있습니다.

하지만 기본적으로 Go 1.12 (2019 년 2 월)를 사용하면이 작업이 더 쉬워 질 것입니다 . 출시 노트를 참조하세요 .

fmt

이제 맵은 테스트를 쉽게하기 위해 키 정렬 순서로 인쇄됩니다 .

주문 규칙은 다음과 같습니다.

  • 적용 가능한 경우 nil은 낮음을 비교합니다.
  • 정수, 부동 소수점 및 문자열 순서 <
  • NaN은 비 NaN 수레보다 적은 수를 비교합니다.
  • boolfalse전에 비교true
  • 복잡함은 실제와 가상의 비교
  • 컴퓨터 주소로 포인터 비교
  • 컴퓨터 주소로 채널 값 비교
  • 구조체는 각 필드를 차례로 비교합니다.
  • 배열은 각 요소를 차례로 비교합니다.
  • 인터페이스 값은 먼저 reflect.Type구체적인 유형 설명하여 비교 한 다음 이전 규칙에서 설명한대로 구체적인 값으로 비교 합니다.

When printing maps, non-reflexive key values like NaN were previously displayed as . As of this release, the correct values are printed.

Sources:

The CL adds: (CL stands for "Change List")

To do this, we add a package at the root, internal/fmtsort, that implements a general mechanism for sorting map keys regardless of their type.

This is a little messy and probably slow, but formatted printing of maps has never been fast and is already always reflection-driven.

The new package is internal because we really do not want everyone using this to sort things. It is slow, not general, and only suitable for the subset of types that can be map keys.

Also use the package in text/template, which already had a weaker version of this mechanism.

You can see that used in src/fmt/print.go#


Disclaimer: Unrelated to map[string]int but related to testing the equivalence of maps in Go, which is the title of the question

If you have a map of a pointer type (like map[*string]int), then you do not want to use reflect.DeepEqual because it will return false.

Finally, if the key is a type that contains an unexported pointer, like time.Time, then reflect.DeepEqual on such a map can also return false.


Use the "Diff" method of github.com/google/go-cmp/cmp:

Code:

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

Output:

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",
+       },
    },
  }

Simplest way:

    assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want)

Example:

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

One of the options is to fix rng:

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

참고URL : https://stackoverflow.com/questions/18208394/how-to-test-the-equivalence-of-maps-in-golang

반응형