2013-08-13 21 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) 
} 

これは、この場合には真であると思われる、同等のマップの文字列表現が同じであることを前提としてい(キーが同じであれば、同じ値にハッシュされるので、その順序は同じになります)。これを行うより良い方法はありますか?テーブル駆動型テストで2つのマップを比較する慣習的な方法は何ですか?

+3

のErr、NO:マップを反復順序は[予測可能]であることが保証されていない(http://golang.org/ref/spec#For_statements ):_ "マップ上の反復順序は指定されておらず、ある反復から次の反復まで同じであることが保証されていません。 – zzzz

+2

さらに、特定のサイズのマップについては、Goを意図的にランダム化します。その順序に依存しないことを強くお勧めします。 –

答えて

77

囲碁ライブラリはすでにあなたがカバーしています。これを行う:あなたはsource codereflect.DeepEqualのための年代Mapケースを見れば

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

が、あなたは両方のマップがnilであれば、彼らは同じ長さを持っている場合、それを最初にチェックが、それは最終的にチェックする前にチェックしていることがわかります彼らは(キー、値)のペアの同じセットを持っているかどうかを確認してください。

reflect.DeepEqualはインターフェイスタイプを取るため、有効なマップ(map[string]bool, map[struct{}]interface{}など)で動作します。マップされていない値でも動作するので、渡すものが本当に2つのマップであることに注意してください。 2つの整数を渡すと、それらが等しいかどうかを幸せに伝えます。

+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

OKですが、 'map [string] float64'のインスタンスを比較したい別のテストケースがあります。 'eq'は' map [string] int'マップに対してのみ機能します。新しいタイプのマップのインスタンスを比較するたびに、 'eq'関数のバージョンを実装する必要がありますか? – andras

+0

@andras:11個のSLOC。私は "貼り付けをコピーする"は、これについて尋ねるよりも短時間でそれを専門にしています。しかし、他の多くの人が同じことをするために「反映」を使用しますが、パフォーマンスはそれほど悪くなります。 – zzzz

+1

地図が同じ順番になるとは思わないでしょうか? https://blog.golangの「反復順序」を保証するものはありません。org/go-maps-in-action – nathj07

1

免責事項map[string]intとは無関係のが、移動中にマップの等価性をテストするに関連し、あなたがポインタ型(のようなmap[*string]int)のマップを持っている場合は、質問

のタイトルで、あなたdo not want to use reflect.DeepEqualそれはfalseを返すためです。

最後に、キーがtime.Timeのような、ポインタが含まれていない型の場合は、can also return falseというマップにreflect.DeepEqualを指定します。

-2

オプションの一つは、RNGを修正することである。

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

downvoteを説明する気に? – Grozz