गोलंग में नक्शों की समानता का परीक्षण कैसे करें?


86

मेरे पास इस तरह एक टेबल-संचालित परीक्षण मामला है:

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

यह मानता है कि समतुल्य मानचित्रों के स्ट्रिंग निरूपण समान होते हैं, जो इस मामले में सही प्रतीत होते हैं (यदि कुंजियाँ समान हैं तो उनके पास समान मान है, इसलिए उनके आदेश समान होंगे)। क्या ऐसा करने के लिए इससे अच्छा तरीका है? टेबल-चालित परीक्षणों में दो मानचित्रों की तुलना करने के लिए मुहावरेदार तरीका क्या है?


4
त्रुटि, नहीं: किसी नक्शे को पुनरावृत्त करने का आदेश पूर्वानुमेय होने की गारंटी नहीं है : "नक्शे पर पुनरावृत्ति क्रम निर्दिष्ट नहीं है और एक पुनरावृत्ति से अगले तक समान होने की गारंटी नहीं है ..."
zzzz

2
इसके अलावा कुछ आकार के नक्शे के लिए जाओ जानबूझकर क्रम यादृच्छिक होगा। यह सलाह दी जाती है कि उस आदेश पर निर्भर न रहें।
जेरेमी वॉल

मानचित्र की तुलना करने की कोशिश करना आपके कार्यक्रम में एक डिज़ाइन दोष है।
इनक गमूस

4
ध्यान दें कि 1.12 (फरवरी 2019) तक, मैप्स को अब परीक्षण को आसान बनाने के लिए कुंजी-क्रमबद्ध क्रम में मुद्रित किया जाता है । देखें नीचे मेरा उत्तर
VonC

जवाबों:


165

गो लाइब्रेरी आपको पहले ही कवर कर चुकी है। यह करो:

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

यदि आप 's केस के सोर्स कोड को देखते हैं, तो आप देखेंगे कि यह पहले चेक करता है कि क्या दोनों मैप्स nil हैं, फिर यह चेक करता है कि क्या उनके पास एक ही लेंथ है या नहीं, यह देखने के लिए कि क्या उनके पास एक ही सेट है (की, मूल्य) जोड़े।reflect.DeepEqualMap

क्योंकि reflect.DeepEqualएक इंटरफ़ेस प्रकार लेता है, यह किसी भी मान्य मानचित्र ( map[string]bool, map[struct{}]interface{}, आदि) पर काम करेगा । ध्यान दें कि यह गैर-मानचित्र मानों पर भी काम करेगा, इसलिए सावधान रहें कि आप जो पास कर रहे हैं वह वास्तव में दो नक्शे हैं। यदि आप इसे दो पूर्णांक पास करते हैं, तो यह आपको खुशी से बताएगा कि क्या वे समान हैं।


बहुत बढ़िया, यह वही है जो मैं देख रहा था। मुझे लगता है कि jnml कह रहा था कि यह प्रदर्शनकारी नहीं है, लेकिन जो एक परीक्षण के मामले में परवाह करता है।
andras

हाँ, यदि आप कभी भी प्रोडक्शन एप्लिकेशन के लिए ऐसा चाहते हैं, तो मैं निश्चित रूप से यदि संभव हो तो एक कस्टम-लिखित फ़ंक्शन के साथ जा सकता हूं, लेकिन यह निश्चित रूप से चाल करता है यदि प्रदर्शन चिंता का विषय नहीं है।
जोशेल

1
@andras तुम भी जांच करनी चाहिए gocheck । जितना सरल c.Assert(m1, DeepEquals, m2)। इसके बारे में क्या अच्छा है यह परीक्षण को निरस्त करता है और आपको बताता है कि आपको आउटपुट में क्या मिला और आपको क्या उम्मीद थी।
ल्यूक

8
यह ध्यान देने योग्य है कि डीपक्वाल को स्लाइस के बराबर होने की भी आवश्यकता है
Xeoncross


13

टेबल-चालित परीक्षणों में दो मानचित्रों की तुलना करने के लिए मुहावरेदार तरीका क्या है?

आपके पास go-test/deepमदद करने के लिए परियोजना है।

लेकिन: यह गो 1.12 (फरवरी 2019) मूल रूप से आसान होना चाहिए : जारी नोट देखें ।

fmt.Sprint(map1) == fmt.Sprint(map2)

fmt

परीक्षण को आसान बनाने के लिए मानचित्र अब मुख्य-क्रमबद्ध क्रम में मुद्रित होते हैं

आदेश देने के नियम हैं:

  • लागू होने पर, शून्य कम तुलना करता है
  • इन्टस, फ्लोट्स और स्ट्रिंग्स ऑर्डर द्वारा <
  • NaN की तुलना गैर-NaN फ़्लोट्स से कम है
  • boolfalseपहले की तुलनाtrue
  • कॉम्प्लेक्स असली, फिर काल्पनिक की तुलना करता है
  • पॉइंटर्स मशीन के पते से तुलना करते हैं
  • चैनल मान मशीन के पते से तुलना करते हैं
  • संरचनाएं बदले में प्रत्येक क्षेत्र की तुलना करती हैं
  • Arrays बदले में प्रत्येक तत्व की तुलना करते हैं
  • पिछले मानों reflect.Typeमें बताए गए ठोस प्रकार और फिर ठोस मूल्य का वर्णन करके इंटरफ़ेस मान पहले तुलना करते हैं।

मैप्स को प्रिंट करते समय, NaN जैसे गैर-रिफ्लेक्सिव प्रमुख मान पहले प्रदर्शित किए गए थे <nil>। इस रिलीज के रूप में, सही मूल्य मुद्रित होते हैं।

सूत्रों का कहना है:

सीएल जोड़ता है: ( सीएल का अर्थ है "परिवर्तन सूची" )

ऐसा करने के लिए, हम रूट पर एक पैकेजinternal/fmtsort जोड़ते हैं , जो कि उनके प्रकार की परवाह किए बिना मानचित्र कुंजियों को छांटने के लिए एक सामान्य तंत्र को लागू करता है।

यह थोड़ा गड़बड़ है और शायद धीमा है, लेकिन नक्शों की स्वरूपित छपाई कभी तेज नहीं रही है और पहले से ही हमेशा प्रतिबिंब-चालित है।

नया पैकेज आंतरिक है क्योंकि हम वास्तव में हर किसी को चीजों को क्रमबद्ध करने के लिए इसका उपयोग नहीं करना चाहते हैं। यह धीमा है, सामान्य नहीं है, और केवल उन प्रकारों के सबसेट के लिए उपयुक्त है जो मानचित्र कुंजी हो सकते हैं।

पैकेज का भी उपयोग करें text/template, जिसमें पहले से ही इस तंत्र का कमजोर संस्करण था।

आप देख सकते हैं कि में इस्तेमाल किया src/fmt/print.go#printValue(): case reflect.Map:


मेरे अज्ञानता के लिए क्षमा करें, मैं गो के लिए नया हूं, लेकिन यह नया fmtव्यवहार नक्शों की समानता का परीक्षण करने में कैसे मदद करता है ? क्या आप उपयोग करने के बजाय स्ट्रिंग अभ्यावेदन की तुलना करने का सुझाव दे रहे हैं DeepEqual?
sschuberth

@sschuberth DeepEqualअभी भी अच्छा है। (या यों कहेंcmp.Equal ) उपयोग के मामले को twitter.com/mikesample/status/108422366262167711744 में सचित्र किया गया है , जैसे मूल मुद्दे में कहा गया है कि लॉग्स को अलग करना: github.com/golang/go/issues/21095 । अर्थ: आपके परीक्षण की प्रकृति के आधार पर, एक विश्वसनीय अंतर मदद कर सकता है।
VonC

fmt.Sprint(map1) == fmt.Sprint(map2)टीएल के लिए; डॉ।
425 नं।

@ 425 धन्यवाद। मैंने उसी के अनुसार उत्तर संपादित किया है।
VonC

11

यह वही है जो मैं करूंगा (अनकहा कोड):

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]float64eqकेवल map[string]intनक्शे के लिए काम करता है । क्या मुझे eqहर बार एक नए प्रकार के नक्शे के उदाहरणों की तुलना करते हुए फ़ंक्शन के एक संस्करण को लागू करना चाहिए ?
andras

@andras: 11 SLOCs। मैं "कॉपी पेस्ट" कर रहा हूँ इससे कम समय में इसे जानने में जितना समय लगता है इसके बारे में पूछें। हालांकि, कई अन्य भी ऐसा करने के लिए "प्रतिबिंबित" का उपयोग करेंगे, लेकिन यह बहुत खराब प्रदर्शन है।
zzzz

1
क्या नक्शे से उसी क्रम में होने की उम्मीद नहीं है? जो जाने की गारंटी नहीं देता है "Iteration order" ब्लॉग
nathj07

3
@ nathj07 नहीं, क्योंकि हम केवल इसके माध्यम से पुनरावृति करते हैं a
टॉरस्टेन ब्रोंगर

5

अस्वीकरण : map[string]intगो में नक्शों की समानता के परीक्षण से संबंधित लेकिन संबंधित, जो प्रश्न का शीर्षक है

आप एक सूचक प्रकार का एक नक्शा है, तो (जैसे map[*string]int), तो आप करते नहीं reflect.DeepEqual उपयोग करना चाहते हैं , क्योंकि यह अवास्तविक लौटाते हैं।

अंत में, यदि कुंजी एक प्रकार है जिसमें समय-समय की तरह एक अस्पष्ट सूचक होता है, तो प्रतिबिंबित करें। इस तरह के मानचित्र पर डीपक्वेल भी गलत वापस आ सकता है


2

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

1

सबसे सरल तरीका:

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

1

इसके बजाय 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

"प्रतिबिंबित का उपयोग करना। परीक्षणों में डीपक्वेल अक्सर एक बुरा विचार है, यही कारण है कि हम http://github.com/google/go-cmp खोलते हैं " - जो त्साइ


-5

विकल्पों में से एक rng को ठीक करना है:

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

क्षमा करें, लेकिन इस प्रश्न से संबंधित आपका उत्तर कैसा है?
दिमा कोज़्विन

@DimaKozhevin गोलंग आंतरिक रूप से मानचित्र में प्रविष्टियों के क्रम को मिलाने के लिए rng का उपयोग करता है। यदि आप rng को ठीक करते हैं तो आपको परीक्षण उद्देश्यों के लिए एक अनुमान लगाने योग्य आदेश मिलेगा।
ग्रूज

@Grozz यह करता है? क्यों!? मैं जरूरी नहीं कि यह विवाद हो (मुझे पता नहीं है) मैं अभी यह नहीं देखता कि यह क्यों होगा।
एमएसनफोर्ड

मैं गोलंग पर काम नहीं करता, इसलिए मैं उनके तर्क की व्याख्या नहीं कर सकता, लेकिन यह कम से कम v1.9 के रूप में एक पुष्ट व्यवहार है। हालाँकि मैंने "हम चाहते हैं कि आप नक्शे में आदेश पर निर्भर नहीं कर सकते हैं, क्योंकि आप नहीं करना चाहिए" की तर्ज पर कुछ स्पष्टीकरण देखा।
ग्रोज़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.