Given the next type structs definition:
type A struct {
Id int
Bs []B
Sub C
}
type B struct {
Id int
Str string
}
type C struct {
Id int
Ds []D
}
type D struct {
Id int
Num int
}
I would like to test if next two instances of A are semantically equal, regardless of the order of the slice elements in all hierarchy levels.
var want = &A{
Id: 1,
Bs: []B{{Id: 10, Str: "b10"}, {Id: 20, Str: "b20"}},
Sub: C{
Id: 100,
Ds: []D{{Id: 101, Num: 1001}, {Id: 102, Num: 1002}},
},
}
var got = &A{
Id: 1,
Bs: []B{{Id: 20, Str: "b20"}, {Id: 10, Str: "b10"}},
Sub: C{
Id: 100,
Ds: []D{{Id: 102, Num: 1002}, {Id: 101, Num: 1001}},
},
}
The assert comparison should return true
The package cmp is intended to be a more powerful and safer alternative to reflect.DeepEqual
for comparing whether two values are semantically equal.
Here is a full implementation of semantic equal structure comparison regardless of slice elements' order at all hierarchy levels.
file source.go
package main
type A struct {
Id int
Bs []B
Sub C
}
type B struct {
Id int
Str string
}
type C struct {
Id int
Ds []D
}
type D struct {
Id int
Num int
}
func NewA() *A {
return &A{
Id: 1,
Bs: []B{{Id: 20, Str: "b20"}, {Id: 10, Str: "b10"}},
Sub: C{
Id: 100,
Ds: []D{{Id: 102, Num: 1002}, {Id: 101, Num: 1001}},
},
}
}
file source_test.go
package main
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
var want = &A{
Id: 1,
Bs: []B{{Id: 10, Str: "b10"}, {Id: 20, Str: "b20"}},
Sub: C{
Id: 100,
Ds: []D{{Id: 101, Num: 1001}, {Id: 102, Num: 1002}},
},
}
func TestNewA(t *testing.T) {
got := NewA()
less := func(x, y any) bool {
switch xv := x.(type) {
case B:
yv := y.(B)
return fmt.Sprintf("%d-%s", xv.Id, xv.Str) < fmt.Sprintf("%d-%s", yv.Id, yv.Str)
case D:
yv := y.(D)
return fmt.Sprintf("%d-%d", xv.Id, xv.Num) < fmt.Sprintf("%d-%d", yv.Id, yv.Num)
default:
return false
}
}
if diff := cmp.Diff(want, got, cmpopts.SortSlices(less)); diff != "" {
t.Errorf("mismatch:\n%s", diff)
}
}