I have a table-driven test case like this one:
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
}
}
I could check whether the lengths are the same and write a loop that checks if every key-value pair is the same. But then I have to write this check again when I want to use it for another type of map (say map[string]string
).
What I ended up doing is, I converted the maps to strings and compared the strings:
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)
}
This assumes that the string representations of equivalent maps are the same, which seems to be true in this case (if the keys are the same then they hash to the same value, so their orders will be the same). Is there a better way to do this? What is the idiomatic way to compare two maps in table-driven tests?
This is what I would do (untested code):
The Go library has already got you covered. Do this:
If you look at the source code for
reflect.DeepEqual
'sMap
case, you'll see that it first checks if both maps are nil, then it checks if they have the same length before finally checking to see if they have the same set of (key, value) pairs.Because
reflect.DeepEqual
takes an interface type, it will work on any valid map (map[string]bool, map[struct{}]interface{}
, etc). Note that it will also work on non-map values, so be careful that what you're passing to it are really two maps. If you pass it two integers, it will happily tell you whether they are equal.You have the project
go-test/deep
to help.But: this should be easier with Go 1.12 (February 2019) natively: See release notes.
Sources:
golang/go
issue 21095,purpleidea
)The CL adds: (CL stands for "Change List")
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#
One of the options is to fix rng:
Disclaimer: Unrelated to
map[string]int
but related to testing the equivalence of maps in Go, which is the title of the questionIf 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.