使用stretchr/testify忽略元素顺序比较含切片字段的Go结构体
Go 多层嵌套结构体(含无序切片)通用比较方案
适用场景
解决protobuf生成的多层嵌套结构体,或普通Go结构体因切片元素顺序不一致导致assert.Equal、reflect.DeepEqual比较失败的问题,无需为每个结构体编写自定义比较函数。
方案1:Protobuf结构体场景(推荐)
Google官方提供的专用比较工具链,原生兼容protobuf生成的结构体,自动忽略内部隐藏字段,支持全局配置切片比较规则:
- 安装依赖
go get github.com/google/go-cmp/cmp go get google.golang.org/protobuf/testing/protocmp
- 测试用例示例
package pkg import ( "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/testing/protocmp" "testing" ) // 原有结构体定义、exp/act构造逻辑保持不变 func Test_Compare(t *testing.T) { exp := &structOne{/* 原有构造逻辑 */} act := &structOne{/* 原有构造逻辑 */} // 配置比较规则:按Identifier排序Offs切片后比较,忽略原始顺序 diff := cmp.Diff(exp, act, protocmp.Transform(), protocmp.SortRepeated(cmp.Comparer(func(a, b *structTwo) bool { return a.Identifier < b.Identifier })), ) if diff != "" { t.Fatalf("结构体匹配失败,差异如下:\n%s", diff) } }
如果有多层不同类型的切片,仅需要新增对应的SortRepeated规则即可,不需要修改其他比较逻辑。
方案2:普通Go结构体场景
如果是手写的普通Go结构体,不需要兼容protobuf特性,可直接使用go-cmp的通用切片排序规则:
import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "testing" ) func Test_Compare(t *testing.T) { exp := &structOne{/* 原有构造逻辑 */} act := &structOne{/* 原有构造逻辑 */} diff := cmp.Diff(exp, act, // 对所有*structTwo类型的切片按Identifier排序后比较 cmpopts.SortSlices(func(a, b *structTwo) bool { return a.Identifier < b.Identifier }), ) assert.Empty(t, diff) }
注意事项
- 不要使用
reflect.DeepEqual比较protobuf生成的结构体:protobuf生成的结构体包含内部隐藏状态字段,DeepEqual会将这些字段纳入比较,大概率出现非预期的匹配失败,官方也明确禁止该用法。 - 支持高级配置:
go-cmp还支持忽略指定字段、自定义字段转换、枚举值比较等扩展能力,可满足绝大多数复杂比较场景。
内容的提问来源于stack exchange,提问作者Dennis van der Veeke
相关产品推荐
相关产品推荐

