go-cmpの各Option紹介
go-cmp
単体テストでオブジェクトを比較する場合reflect.DeepEqual
を使用するのが一般的です。
reflect.DeepEqual
はオブジェクト完全に一致していないとfalseになってしまいますが、
実際にオブジェクトを比較するときは、以下のように柔軟に比較したいことがよくあります。
privateフィールドは比較したくない or 比較したい
時間のフィールドは比較したくない
順番が一致していなくてもSortして一致していれば問題ない
また大きなオブジェクトをreflect.DeepEqual
で比較すると、差分を調べるのがとても大変です。
そういった細かい条件を設定してオブジェクトを比較してくれるのがgo-cmpです。
複雑なオブジェクトを比較してみる
reflect.DeepEqual
と異なり、差分がとても見やすいのところがgo-cmpの魅力の一つです。
func TestCmp(t *testing.T) {
type ComplexObject struct {
ID int64
Name string
Score float64
Public bool
RelationIds []int64
}
type ComplexObjects []ComplexObject
type Compare struct {
Name string
Value int
Objects ComplexObjects
}
v1 := &Compare{
Name: "Tom",
Value: 100,
Objects: ComplexObjects{
{
ID: 1,
Name: "xyz",
Score: 1.0,
Public: true,
RelationIds: []int64{1, 2, 3, 4, 5},
},
{
ID: 2,
Name: "xxxxx",
Score: 99.9,
Public: true,
RelationIds: []int64{10, 20, 30, 40, 50},
},
},
}
v2 := &Compare{
Name: "Andrew",
Value: 50,
Objects: ComplexObjects{
{
ID: 1,
Name: "xyz",
Score: 1.0,
Public: true,
RelationIds: []int64{1, 2, 3, 4, 5},
},
{
ID: 3,
Name: "abc",
Score: 11.1,
Public: false,
RelationIds: []int64{6, 7, 8, 9, 10},
},
},
}
if diff := cmp.Diff(v1, v2); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- FAIL: TestCmp (0.00s)
example_test.go:68: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
- Name: "Tom",
+ Name: "Andrew",
- Value: 100,
+ Value: 50,
Objects: gocmp.ComplexObjects{
{ID: 1, Name: "xyz", Score: 1, Public: true, ...},
- {
- ID: 2,
- Name: "xxxxx",
- Score: 99.9,
- Public: true,
- RelationIds: []int64{10, 20, 30, 40, 50},
- },
+ {ID: 3, Name: "abc", Score: 11.1, RelationIds: []int64{6, 7, 8, 9, 10}},
},
}
--- FAIL: TestCmp (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.002s
各種オプション紹介
go-cmpには様々な機能があります。今回はgo-cmpが比較の際に使用する各Optionの使い方をサンプルコードを添えて紹介しようとおもいます。
- https://pkg.go.dev/github.com/google/go-cmp@v0.5.4/cmp
- https://pkg.go.dev/github.com/google/go-cmp@v0.5.4/cmp/cmpopts
cmp.AllowUnexported()
構造体がprivateフィールドを持つ場合は、以下のオプションのどちらかを指定する必要があります。
cmp.AllowUnexported
cmpopts.IgnoreUnexported
cmp.AllowUnexported
はprivateフィールを比較対象にする場合に使用するオプションです。下記のテストではprivateフィールドが異なるためテストが失敗します。
func TestAllowUnexported(t *testing.T) {
type Compare struct {
Exported int
unexported int
}
v1 := &Compare{
Exported: 100,
unexported: 1,
}
v2 := &Compare{
Exported: 100,
unexported: 2,
}
opt := cmp.AllowUnexported(Compare{})
if diff := cmp.Diff(v1, v2, opt); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- FAIL: TestAllowUnexported (0.00s)
example_test.go:25: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
Exported: 100,
- unexported: 1,
+ unexported: 2,
}
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.003s
cmpopts.AcyclicTransformer
cmpopts.AcyclicTransformer
はオブジェクトの指定したフィールドに変換処理を施した後に比較することができます。 下記のサンプルコードでは、オブジェクトのSplitLinesフィールを,
で分割したのちに比較しています。
下記のサンプルではあえてエラーになる値で実験します。 分割なしと分割ありでエラー差分のメッセージがことなります。 分割なしでは「Sliceが異なる」というメッセージですが、分割ありでは「Slice内のある文字(要素)が異なる」に変わります。 長いデータを比較する際は、cmpopts.AcyclicTransformer
を使用して分割すると、差分を見つけやすくなると思われます。
分割なし
func TestAcyclicTransformer(t *testing.T) {
type Compare struct {
SplitLines interface{}
}
v1 := &Compare{
SplitLines: "1,2,3",
}
v2 := &Compare{
SplitLines: "1,10,3",
}
// func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option
opts := []cmp.Option{
// cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
// return strings.Split(s, ",")
// }),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
example_test.go:445: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
- SplitLines: string("1,2,3"),
+ SplitLines: string("1,10,3"),
}
--- FAIL: TestAcyclicTransformer (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.002s
分割あり
func TestAcyclicTransformer(t *testing.T) {
type Compare struct {
SplitLines interface{}
}
v1 := &Compare{
SplitLines: "1,2,3",
}
v2 := &Compare{
SplitLines: "1,10,3",
}
// func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
return strings.Split(s, ",")
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
example_test.go:446: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
SplitLines: string(Inverse(SplitLines, []string{
"1",
- "2",
+ "10",
"3",
})),
}
--- FAIL: TestAcyclicTransformer (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.002s
IgnoreXXXX
cmpopts.IgnoreUnexported
cmp.AllowUnexported
ではprivateフィールドも比較したためテストが失敗しました。 今度はcmpops.IgnoreUnexported
を使用してみます。 privateフィールドの比較がなくなったのでテストが通ります。
func TestIgnoreUnexported(t *testing.T) {
type Compare struct {
Exported int
unexported int
}
v1 := &Compare{
Exported: 100,
unexported: 1,
}
v2 := &Compare{
Exported: 100,
unexported: 2,
}
// func IgnoreUnexported(typs ...interface{}) cmp.Option
opt := cmpopts.IgnoreUnexported(Compare{})
if diff := cmp.Diff(v1, v2, opt); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreUnexported (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp
cmpopts.IgnoreFields
オブジェクトのフィールドに時間が含まれているときは、比較対象から外したいことがあります。 cmpopts.IgnoreFields
を使用すると特定のフィールドのみ比較対象から外すことができます。
func TestIgnoreFields(t *testing.T) {
type Compare struct {
Exported int
CreatedAt time.Time
UpdatedAt time.Time
}
v1 := &Compare{
Exported: 100,
CreatedAt: time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC),
}
v2 := &Compare{
Exported: 100,
CreatedAt: time.Date(3100, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(3100, 1, 1, 0, 0, 0, 0, time.UTC),
}
// func IgnoreFields(typ interface{}, names ...string) cmp.Option
opts := []cmp.Option{
cmpopts.IgnoreFields(Compare{}, "CreatedAt", "UpdatedAt"),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreFields (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.002s
cmpopts.IgnoreTypes
さきほどはフィールド名をしてして、比較対象から外しましたが、型を指定して比較対象から外すこともできます。cmpopts.IgnoreTypes
を使用してtime.Time
を外すことで異なる時間をもったオブジェクトを比較してもテストが通ります。
func TestIgnoreTypes(t *testing.T) {
type Compare struct {
Exported int
CreatedAt time.Time
UpdatedAt time.Time
}
v1 := &Compare{
Exported: 100,
CreatedAt: time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC),
}
v2 := &Compare{
Exported: 100,
CreatedAt: time.Date(3100, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(3100, 1, 1, 0, 0, 0, 0, time.UTC),
}
// func IgnoreTypes(typs ...interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.IgnoreTypes(time.Time{}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreTypes (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.003s
cmpopts.IgnoreSliceElements
cmpopts.IgnoreSliceElementsを使用するとsliceの特定の要素を無視して比較することもできます。下記のサンプルはsliceの9
を無視して比較したものです。v2のSliceは9を無視すると1,2,3,4,5
になり一致します
func TestIgnoreSliceElements(t *testing.T) {
type Compare struct {
Exported int
Slice []int
}
v1 := &Compare{
Exported: 100,
Slice: []int{1, 2, 3, 4, 5},
}
v2 := &Compare{
Exported: 100,
Slice: []int{1, 9, 2, 9, 3, 9, 4, 9, 5},
}
// func IgnoreSliceElements(discardFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.IgnoreSliceElements(func(elem int) bool {
return elem == 9
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreSliceElements (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp
cmpopts.IgnoreMapEntries
func TestIgnoreMapEntries(t *testing.T) {
type Compare struct {
Exported int
Map map[string]int
}
v1 := &Compare{
Exported: 100,
Map: map[string]int{
"A": 1,
"B": 1,
},
}
v2 := &Compare{
Exported: 100,
Map: map[string]int{
"B": 1,
"A": 1,
"X": 9999,
},
}
// func IgnoreMapEntries(discardFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.IgnoreMapEntries(func(key string, val int) bool {
return key == "X" && val == 9999
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreMapEntries (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.003s
cmpopts.cmpopts.IgnoreInterfaces
cmpopts.cmpopts.IgnoreInterfaces
は、比較対象のオブジェクトから特定のInterfaceを比較しないよう除外設定できます。cmpopts.IgnoreInterfaces(struct{ InterfaceA }{})
のように設定するとCompareオブジェクトを比較する際ににInterfaceA型フィールドを無視して比較することができます。
type InterfaceA interface {
Get() int
}
type InterfaceAImpl struct {
X int
}
func (i *InterfaceAImpl) Get() int {
return i.X
}
type InterfaceB interface {
Set(x int)
}
type InterfaceBImpl struct {
X int
}
func (i *InterfaceBImpl) Set(x int) {
i.X = x
}
func TestIgnoreInterfaces(t *testing.T) {
type Compare struct {
Exported int
IA InterfaceA
IB InterfaceB
}
v1 := &Compare{
Exported: 100,
IA: &InterfaceAImpl{X: 1},
IB: &InterfaceBImpl{X: 1},
}
v2 := &Compare{
Exported: 100,
IA: &InterfaceAImpl{X: 100},
IB: &InterfaceBImpl{X: 1},
}
// func IgnoreInterfaces(ifaces interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.IgnoreInterfaces(struct{ InterfaceA }{}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestIgnoreInterfaces (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.002s
EquoteXXX
cmpopts.EquateEmpty
cmpopts.EquateEmpty
を使用すると空のMapやSliceとnilを同値とみなして比較することができます。
func TestEquoteEmpty(t *testing.T) {
type Compare struct {
Exported int
Slice []int
Map map[string]int
}
v1 := &Compare{
Exported: 100,
Slice: []int{},
Map: map[string]int{},
}
v2 := &Compare{
Exported: 100,
Slice: nil,
Map: nil,
}
// func EquateEmpty() cmp.Option
opts := []cmp.Option{
cmpopts.EquateEmpty(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteEmpty (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.002s
cmpopts.EquateNaNs
cmpopts.EquateNaNs
はfloat32/float64のNaN
を同値として扱って比較してくれます。
v1もv2もmath.NaN()
なのだからオプション無しでも良いのでは?と思うかもしれませんがオプションを設定しないとテストは失敗します。
オプションなし
func TestEquoteNans(t *testing.T) {
type Compare struct {
Exported float64
}
v1 := &Compare{
Exported: math.NaN(),
}
v2 := &Compare{
Exported: math.NaN(),
}
// func EquateNaNs() cmp.Option
opts := []cmp.Option{
// cmpopts.EquateNaNs(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
=== RUN TestEquoteNans
example_test.go:317: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
- Exported: NaN,
+ Exported: NaN,
}
--- FAIL: TestEquoteNans (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.003s
オプションあり
func TestEquoteNans(t *testing.T) {
type Compare struct {
Exported float64
}
v1 := &Compare{
Exported: math.NaN(),
}
v2 := &Compare{
Exported: math.NaN(),
}
// func EquateNaNs() cmp.Option
opts := []cmp.Option{
cmpopts.EquateNaNs(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteNans (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp (cached)
cmpopts.EquateErrors
go-cmpはデフォルトではerrorの比較ができません。そこでcmpopts.EquateErrors
を使用するとエラーを比較できるようになります。同値と見なされるには、同じエラーオブジェクトである必要があります。(メッセージだけ一致していても比較失敗します)
オプションなし
func TestEquoteErrors(t *testing.T) {
type Compare struct {
Error error
}
err := errors.New("error occured")
v1 := &Compare{
Error: err,
}
v2 := &Compare{
Error: err,
}
// func EquateErrors() cmp.Option
opts := []cmp.Option{
// cmpopts.EquateErrors(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- FAIL: TestEquoteErrors (0.00s)
panic: cannot handle unexported field at {*gocmp.Compare}.Error.(*errors.errorString).s:
"errors".errorString
consider using cmpopts.EquateErrors to compare error values [recovered]
panic: cannot handle unexported field at {*gocmp.Compare}.Error.(*errors.errorString).s:
"errors".errorString
consider using cmpopts.EquateErrors to compare error values
オプションあり 別オブジェクト
func TestEquoteErrors(t *testing.T) {
type Compare struct {
Error error
}
v1 := &Compare{
Error:errors.New("error occured"),
}
v2 := &Compare{
Error: errors.New("error occured"),
}
// func EquateErrors() cmp.Option
opts := []cmp.Option{
cmpopts.EquateErrors(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
example_test.go:339: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
- Error: &⟪0xc00006e630⟫"errors".errorString{s: "error occured"},
+ Error: &⟪0xc00006e650⟫"errors".errorString{s: "error occured"},
}
--- FAIL: TestEquoteErrors (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.003s
オプションあり
func TestEquoteErrors(t *testing.T) {
type Compare struct {
Error error
}
err := errors.New("error occured")
v1 := &Compare{
Error: err,
}
v2 := &Compare{
Error: err,
}
// func EquateErrors() cmp.Option
opts := []cmp.Option{
cmpopts.EquateErrors(),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteErrors (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp
cmpopts.EquateApprox(fraction, margin float64)
cmpopts.EquateApprox(fraction, margin float64)
は少数の差が一定の範囲内ならば同値とみなす設定ができます。引数のfractionとmarginは右式のように使用されます|x-y| ≤ max(fraction*min(|x|, |y|), margin)
- fraction=0, margin=0.01 にすると、差分が0.01内であれば同値と見なします。
- fraction=10, margin=0にすると、差分が1/10であれば同値とみなします。
func TestEquoteApprox(t *testing.T) {
type Compare struct {
Exported float64
}
v1 := &Compare{
Exported: 0.1,
}
v2 := &Compare{
Exported: 0.01,
}
// func EquateApprox(fraction, margin float64) cmp.Option
opts := []cmp.Option{
cmpopts.EquateApprox(0, 0.091),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteApprox_fraction_margin (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp
func TestEquoteApprox(t *testing.T) {
type Compare struct {
Exported float64
}
v1 := &Compare{
Exported: 0.1,
}
v2 := &Compare{
Exported: 0.01,
}
// func EquateApprox(fraction, margin float64) cmp.Option
opts := []cmp.Option{
cmpopts.EquateApprox(10, 0),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteApprox_fraction_margin (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp
cmpopts.EquateApproxTime
cmpopts.EquateApproxTime
は、時間を比較する際にその差が、引数の時間(time.Duration)以内であれば同値と見なすことができます
func TestEquoteApproxTime(t *testing.T) {
type Compare struct {
Exported time.Time
}
v1 := &Compare{
Exported: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
}
v2 := &Compare{
Exported: time.Date(2021, 1, 1, 0, 0, 0, 10, time.UTC),
}
// func EquateApproxTime(margin time.Duration) cmp.Option
opts := []cmp.Option{
cmpopts.EquateApproxTime(time.Second * 10),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestEquoteApproxTime (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.0
SortXXXX
cmpopts.SortSlices
cmpopts.SortSlices
は引数で渡した関数でSliceをソートしてから比較することができます。引数でわたす無名関数の型と異なるSliceはソートされないため、下記の例に順番が異なるstringのSliceを追加すると同値にならずエラーになります。
func TestSortSlice(t *testing.T) {
type Compare struct {
NumSlice []int
}
v1 := &Compare{
NumSlice: []int{1, 2, 3, 4, 5},
}
v2 := &Compare{
NumSlice: []int{5, 4, 3, 2, 1},
}
// func SortSlices(lessFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.SortSlices(func(i, j int) bool {
return i < j
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
--- PASS: TestSortSlice (0.00s)
PASS
ok github.com/tMinamiii/sandbox-go/gocmp 0.002
func TestSortSlice(t *testing.T) {
type Compare struct {
NumSlice []int
StringSlice []string
}
v1 := &Compare{
NumSlice: []int{1, 2, 3, 4, 5},
StringSlice: []string{"1", "2", "3"},
}
v2 := &Compare{
NumSlice: []int{5, 4, 3, 2, 1},
StringSlice: []string{"3", "2", "1"},
}
// func SortSlices(lessFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.SortSlices(func(i, j int) bool {
return i < j
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
example_test.go:403: Compare value is mismatch (-v1 +v2): &gocmp.Compare{
NumSlice: Inverse(cmpopts.SortSlices, []int{1, 2, 3, 4, ...}),
StringSlice: []string{
- "1",
+ "3",
"2",
- "3",
+ "1",
},
}
--- FAIL: TestSortSlice (0.00s)
FAIL
FAIL github.com/tMinamiii/sandbox-go/gocmp 0.003s
cmpopts.SortMaps
cmpopts.SortMaps
は引数で渡した関数でMapをソートしてから比較することができます。ソートに用いるのはmapのKey値です。下記のサンプルだとstring型なのでcmpopts.SortMaps(func(i, j string) bool
という関数でソートします。ただし、Mapが同じかどうかに要素順番は無関係なので、cmpopts.SortMaps
は比較目的ではなく別の用途で使われると思われます。
func TestSortMaps(t *testing.T) {
v1 := map[string]int{
"AAA": 111,
"BBB": 222,
"CCC": 333,
}
v2 := map[string]int{
"BBB": 222,
"CCC": 333,
"AAA": 111,
}
// func SortMaps(lessFunc interface{}) cmp.Option
opts := []cmp.Option{
cmpopts.SortMaps(func(i, j string) bool {
return i < j
}),
}
if diff := cmp.Diff(v1, v2, opts...); diff != "" {
t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}
}
おわりに
検索してもサンプルコードや情報が少ないので全オプションを紹介してみました。今回はオプションのみの紹介ですが、Diff以外の比較機能についても後日紹介しようとおもいます。 go-cmpを使いこなして可読性の高いテストコードを作成しましょう!
Discussion