Go语言 将切片字段忽略项顺序的结构与stretchr/testi进行比较

zlhcx6iw  于 2023-04-18  发布在  Go
关注(0)|答案(2)|浏览(172)

我有一个问题,我需要比较两个非常大的结构体(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))。我正在寻找一种方法来比较这样的结构,最好不需要为所有对象创建自定义比较函数。
谢谢你

t98cgbkg

t98cgbkg1#

您可以使用assert.ElementsMatch来比较两个切片**,而不考虑**元素的顺序。
ElementsMatchAssert指定的listA(数组,切片...)等于指定的listB(数组,切片...),忽略元素的顺序。如果有重复的元素,则它们在两个列表中出现的次数应该匹配。
但是这只适用于slice字段本身。如果你的struct模型有很少的字段,你可以逐个比较它们,然后在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,您可以这样使用。默认情况下忽略顺序或切片项。

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

ddrv8njm2#

你可以通过使用cmpopts包的IgnoreFields函数来实现这一点。你需要指定哪些结构中的哪些字段需要被忽略。
名称可以是点分隔字符串(例如,"Foo.Bar"),以忽略嵌入或嵌套在父结构中的特定子字段。
下面是一个例子:

got, want := SomeFunc()

if !cmp.Equal(want, got, cmpopts.IgnoreFields(Struct1{}, "Field1", "Field2", "Struct2.FieldA")) {
    t.Errorf("SomeFunc() mismatch")
}

相关问题