このようなテーブル駆動のテストケースがあります。
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つのマップを比較する慣用的な方法は何ですか?
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つのマップであることに注意してください。 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
}
テーブル駆動テストで2つのマップを比較する慣用的な方法は何ですか?
プロジェクト go-test/deep
があります。
しかし:Go 1.12(2019年2月)natively: リリースノート 。
fmt
テストを容易にするために、マップはキーでソートされた順に印刷されるようになりました。
順序付け規則は次のとおりです。
- 該当する場合、nilはlowと比較します
<
によるint、float、stringsの順序- NaNは、NaN以外の浮動小数点数と比較します
bool
はfalse
をtrue
の前に比較します- 複素数は実数と虚数を比較します
- ポインタはマシンアドレスで比較します
- チャネル値はマシンアドレスで比較します
- 構造体は各フィールドを順番に比較します
- 配列は各要素を順番に比較します
- インターフェース値は、最初に
reflect.Type
で具体的なタイプを記述し、次に前のルールで説明した具体的な値で比較します。マップを印刷するとき、NaNなどの非再帰キー値は以前はとして表示されていました。このリリースでは、正しい値が出力されます。
ソース:
golang/go
issue 21095 、purpleidea
)CLは以下を追加します:( CLは「変更リスト」を表します )
これを行うには、 ルートにあるパッケージ、
internal/fmtsort
を追加します。これは、タイプに関係なくマップキーをソートするための一般的なメカニズムを実装します。これは少し面倒で、おそらく遅いかもしれませんが、マップのフォーマットされた印刷は決して高速ではなく、すでにリフレクション駆動型です。
新しいパッケージは内部のものです。これを使用するすべての人が物事をソートすることを本当に望まないからです。一般的ではなく低速であり、マップキーにできる型のサブセットにのみ適しています。
また、text/template
のパッケージも使用します。このパッケージには、このメカニズムの脆弱なバージョンが既に含まれています。
src/fmt/print.go#
で使用されていることがわかります
免責事項:map[string]int
とは関係ありませんが、Goのマップの等価性のテストに関係しています。これは質問のタイトルです
ポインタータイプのマップ(map[*string]int
など)がある場合は、 donotを使用してReflect.DeepEqualを使用します falseを返すため。
最後に、キーがtime.Timeのようなエクスポートされていないポインターを含むタイプである場合、そのようなマップでreflect.DeepEqual falseを返すこともできます 。
この回答 に触発され、Golangのマップの等価性をテストするために以下を使用します。
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)
}
}
github.com/google/go-cmp/cmp の「Diff」メソッドを使用します。
コード:
// 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",
+ },
},
}