A conversation came up at work about how people preferred to compare and diff objects when writing go tests. I was assigned to do a quick comparison.
Given the following inputs:
package main
import (
"fmt"
"reflect"
"strings"
"github.com/go-test/deep"
"github.com/google/go-cmp/cmp"
)
type MyStruct struct {
a []string
b string
c bool
d *SubStruct
e map[string]string
}
type SubStruct struct {
a string
}
var (
obj1 = MyStruct{
a: []string{"foo", "bar", "baz"},
b: "red",
c: false,
d: &SubStruct{
"obj1",
},
e: map[string]string{
"France": "Paris",
"Germany": "Berlin",
"Netherlands": "Amsterdam",
},
}
obj2 = MyStruct{
a: []string{"foo", "baz"},
b: "blue",
c: true,
d: &SubStruct{
"obj2",
},
e: map[string]string{
"France": "Lyon",
"Germany": "Bonn",
"Netherlands": "Den Haag",
"England": "Manchester",
},
}
)
func main() {
fmt.Println("=== Stdlib ===\n")
if !reflect.DeepEqual(obj1, obj2) {
fmt.Printf("obj1 = %#v;\nobj2 = %#v\n", obj1, obj2)
}
fmt.Println("\n=== deep.Equal ===\n")
deep.CompareUnexportedFields = true
if diff := deep.Equal(obj1, obj2); diff != nil {
fmt.Printf("obj1 != obj2:\n%v\n", strings.Join(diff, "\n"))
}
fmt.Println("\n=== go-cmp ===\n")
if diff := cmp.Diff(obj1, obj2, cmp.AllowUnexported(obj1, *obj1.d)); diff != "" {
fmt.Printf("obj1 != obj2:\n%v\n", diff)
}
}
Output
=== Stdlib ===
obj1 = main.MyStruct{a:[]string{"foo", "bar", "baz"}, b:"red", c:false, d:(*main.SubStruct)(0x124e350), e:map[string]string{"France":"Paris", "Germany":"Berlin", "Netherlands":"Amsterdam"}};
obj2 = main.MyStruct{a:[]string{"foo", "baz"}, b:"blue", c:true, d:(*main.SubStruct)(0x124e360), e:map[string]string{"England":"Manchester", "France":"Lyon", "Germany":"Bonn", "Netherlands":"Den Haag"}}
=== deep.Equal ===
obj1 != obj2:
a.slice[1]: bar != baz
a.slice[2]: baz != <no value>
b: red != blue
c: false != true
d.a: obj1 != obj2
e.map[France]: Paris != Lyon
e.map[Germany]: Berlin != Bonn
e.map[Netherlands]: Amsterdam != Den Haag
e.map[England]: <does not have key> != Manchester
=== go-cmp ===
obj1 != obj2:
main.MyStruct{
a: []string{
"foo",
- "bar",
"baz",
},
- b: "red",
+ b: "blue",
- c: false,
+ c: true,
- d: &main.SubStruct{a: "obj1"},
+ d: &main.SubStruct{a: "obj2"},
e: map[string]string{
+ "England": "Manchester",
- "France": "Paris",
+ "France": "Lyon",
- "Germany": "Berlin",
+ "Germany": "Bonn",
- "Netherlands": "Amsterdam",
+ "Netherlands": "Den Haag",
},
}