比较具有忽略项目顺序的切片字段的结构与stretchr/testify

Posted

技术标签:

【中文标题】比较具有忽略项目顺序的切片字段的结构与stretchr/testify【英文标题】:Compare structs that have slice fields ignoring item order with stretchr/testify 【发布时间】:2021-12-26 13:17:38 【问题描述】:

我有一个问题,我需要将两个非常大的结构(生成的 protobuf)相互比较,作为测试用例的一部分。这些结构中有多个嵌套数组。下面是重现/演示问题的简化示例。

package pkg

import (
    "github.com/stretchr/testify/assert"
    "reflect"
    "testing"
)

type structOne struct 
    Foo  string
    Offs []*structTwo


type structTwo struct 
    Identifier string


func Test_Compare(t *testing.T) 
    exp := &structOne
        Foo: "bar",
        Offs: []*structTwo
            
                Identifier: "one",
            ,
            
                Identifier: "two",
            ,
            
                Identifier: "three",
            ,
            
                Identifier: "four",
            ,
        ,
    

    act := &structOne
        Foo: "bar",
        Offs: []*structTwo
            
                Identifier: "four",
            ,
            
                Identifier: "three",
            ,
            
                Identifier: "two",
            ,
            
                Identifier: "one",
            ,
        ,
    

    assert.Equal(t, exp, act)                   // fails
    assert.True(t, reflect.DeepEqual(exp, act)) // fails

我尝试过使用assert.Equal(t, exp, act)assert.True(t, reflect.DeepEqual(exp, act))。我正在寻找一种比较此类结构的方法,最好不必为所有对象创建自定义比较函数。

谢谢

【问题讨论】:

【参考方案1】:

您可以使用assert.ElementsMatch 比较两个切片不考虑元素排序。

ElementsMatch 断言指定的 listA(array, slice...) 等于指定的 listB(array, slice...) 忽略元素的顺序。如果存在重复元素,则它们在两个列表中的出现次数应匹配。

但这仅适用于切片字段本身。如果你的结构体模型的字段很少,你可以一一比较,在切片上使用ElementsMatch

    assert.Equal(t, exp.Foo, act.Foo)
    assert.ElementsMatch(t, exp.Offs, act.Offs)

如果你的结构体有很多字段,你可以将切片值重新赋值给临时变量,nil将字段取出,然后比较:

    expOffs := exp.Offs
    actOffs := act.Offs

    exp.Offs = nil
    act.Offs = nil

    assert.Equal(t, exp, act) // comparing full structs without Offs
    assert.ElementsMatch(t, expOffs, actOffs) // comparing Offs separately

如果stretchr/testify 允许为用户定义的类型注册自定义比较器,或者检查对象是否实现某个接口并调用它来测试相等性,那就更好了

if cmp, ok := listA.(Comparator); ok  
    cmp.Compare(listB) 

但我不知道有这样的功能。


另外,建议使用https://github.com/r3labs/diff,您可以照此使用。默认情况下会忽略 order 或 slice 项。

    // import "github.com/r3labs/diff/v2"
    changelog, err := diff.Diff(exp, act)
    assert.NoError(t, err)
    assert.Len(t, changelog, 0)

【讨论】:

以上是关于比较具有忽略项目顺序的切片字段的结构与stretchr/testify的主要内容,如果未能解决你的问题,请参考以下文章

golang超级mapper包 - coven

go 学习 3

比较引用类型对象的集合是不是相等,忽略集合中项目的顺序

比较 XML 忽略元素顺序

06-03线性结构与切片

06-03线性结构与切片