我有一个像这样的表驱动测试用例:
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)
}
这假设等效映射的字符串表示是相同的,在这种情况下似乎是正确的(如果键是相同的,那么它们散列到相同的值,因此它们的顺序将是相同的)。有一个更好的方法吗?在表格驱动的测试中比较两个地图的惯用方法是什么?
答案 0 :(得分:118)
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
reflect.DeepEqual
个案的source code,您会看到它首先检查两个地图是否为零,然后检查它们是否相同在最终检查它们是否具有相同的(键,值)对之前的长度。
由于map[string]bool, map[struct{}]interface{}
采用接口类型,因此它适用于任何有效的地图({{1}}等)。请注意,它也适用于非地图值,因此请注意,传递给它的是两个地图。如果你传递两个整数,它会很高兴地告诉你它们是否相等。
答案 1 :(得分:9)
这就是我要做的(未经测试的代码):
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 :(得分:6)
在表驱动测试中比较两个地图的惯用方式是什么?
您有项目go-test/deep
可以提供帮助。
但是:使用 Go 1.12 (2019年2月)
fmt
现在可以按键顺序打印地图,以简化测试。
订购规则为:
- 在适用时,nil比较低
- 整数,浮点数和字符串按
<
的顺序- NaN的比较少于非NaN的浮动
bool
在false
之前比较true
- Complex比较实数,然后是虚数
- 指针按机器地址进行比较
- 通道值按机器地址进行比较
- 结构依次比较每个字段
- 数组依次比较每个元素
- 接口值首先通过描述具体类型的
reflect.Type
进行比较,然后按照先前规则中的具体值进行比较。在打印地图时,非自反键值(如NaN)以前显示为。从此版本开始,将打印正确的值。
来源:
CL添加:(CL stands for "Change List")
为此,我们添加了a package at the root,
internal/fmtsort
,该方法实现了一种用于对映射键进行排序的通用机制,而不论其类型如何。这有点混乱并且可能很慢,但是地图的格式化打印从未如此快速,而且一直都是反射驱动的。
新软件包是内部的,因为我们确实不希望每个人都使用此软件包对事物进行排序。它很慢,不通用,仅适用于可以作为映射键的类型的子集。
还可以使用text/template
中的软件包,该软件包已经具有较弱的版本。
您可以看到src/fmt/print.go#
答案 3 :(得分:4)
免责声明:与map[*string]int
无关,但与测试Go中地图的等效性有关,这是问题的标题
如果你有一个指针类型的地图(如varibale=$(command)
),那么你do not want to use reflect.DeepEqual因为它会返回false。
最后,如果键是包含未导出指针的类型,例如time.Time,则在这样的地图can also return false上反射.DeepEqual。
答案 4 :(得分: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)
}
答案 5 :(得分: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
“在测试中使用reflect.DeepEqual通常不是一个好主意,这就是为什么我们开源http://github.com/google/go-cmp” -蔡仔
答案 6 :(得分:0)
受this answer的启发,我使用以下代码测试了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)
}
}
答案 7 :(得分:-1)
使用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",
+ },
},
}
答案 8 :(得分:-5)
其中一个选项是修复rng:
rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))