go-cmpを使ったTips

publish: 2022-09-19

Go のテストを書く時、期待値として構造体を比較したいときが多く発生すると思います。

特に時刻情報などテストで扱う場合、テストを書くレベルが高くなるかと思います。

そんな時におすすめなのが go-cmp になります。

go-cmp を使わないで書く場合

例えば以下のような構造体がある場合、

type User struct {
ID uint64
Nmae string
CreateAt time.Time
UpdateAt time.Time
}

go-cmp を使わないでテストを書く場合、このようになる。

func TestNoGoCmpUser(t *testing.T) {
user1 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now(),
}
user2 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now(),
}
if user1.ID != user2.ID {
t.Errorf("user1 %v, but user2 %v", user1.ID, user2.ID)
}
if user1.Name != user2.Name {
t.Errorf("user1 %v, but user2 %v", user1.Name, user2.Name)
}
}
この場合`CreateAt`など時刻を比較する場合エラーになってしまう。
そのような問題を解決するときに、`go-cmp`を使うと解決できる。

メリット

go-cmp を使うメリットとしては以下の場合があるかと思います。

ちなみに Go のテストでは、reflect.DeepEqualがありますが、

こちらの go-cmp は上位互換に位置しているものです。

構造体から一部を除外したい場合

この場合、CreateAt UpdateAt を除外すると以下のように記述できます。

func TestUser(t *testing.T) {
user1 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now(),
}
user2 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now(),
}
opt := cmpopts.IgnoreFields(User{}, "CreateAt", "UpdateAt")
if diff := cmp.Diff(user1, user2, opt); diff != "" {
t.Errorf("X value is mismatch (-num1 +num2):%s\n", diff)
}
}

cmpopts.IgnoreFieldsに除外したい値を入れることで除外できます。

緩く時間チェックする場合

先ほどは時刻の値をチェックしていましたが、もう少し緩くチェックしたい場合もあると思います。

そんな時は、cmpopts.EquateApproxTimeを使用します。

func TestUser1(t *testing.T) {
user1 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now().Add(time.Duration(1) * time.Second),
}
user2 := User{
ID: 1,
Name: "taro",
CreateAt: time.Now().Add(time.Duration(2) * time.Second),
}
// エラー
// output
// ID: 1,
// Name: "taro",
// - CreateAt: s"2022-09-19 20:51:58.980243 +0900 JST m=+1.003101001",
// + CreateAt: s"2022-09-19 20:51:59.980243 +0900 JST m=+2.003101167",
// UpdateAt: s"0001-01-01 00:00:00 +0000 UTC",
// }
if diff := cmp.Diff(user1, user2); diff != "" {
t.Errorf("X value is mismatch (-num1 +num2):%s\n", diff)
}
if diff := cmp.Diff(user1, user2, cmpopts.EquateApproxTime(5*time.Second)); diff != "" {
t.Errorf("error case:\n(-got +want)\n%v", diff)
}
}