🧪

【Go】Goのテストに入門してみた! ~go-cmp編~

に公開

はじめに

前回は「Testify」を見ていきました。
今回は「go-cmp」に入門します!

前回の記事はこちら!

この記事でわかること

  • go-cmp の概要
  • go-cmp を使った構造体比較の基本
  • go-cmp を使った複合体を含む構造体比較の方法

go-cmpとは?

go-cmp は、Googleが提供しているGo用の比較ライブラリです。
標準の reflect.DeepEqual() よりも柔軟で細かい制御が必要なケースで活躍します。
特徴としては以下のような点があります。

  • 複雑な構造体やネストされたデータでも比較できる
  • フィールド単位で無視(Ignore)や変換(Transformer)を指定できる
  • 比較結果を見やすい差分(diff)として表示できる

比較結果が差分として出力されるため、どこが違うのか一目で確認できます。

go-cmpを使った構造体比較の基本

基底型のみで構成されている構造体を、go-cmp を使って比較してみます。

main_test.go
package main

import (
    "testing"
    
    "github.com/google/go-cmp/cmp"
)

type User struct {
    ID int
    Name string
    Age int
}

func TestStruct(t *testing.T) {
    u1 := User{ID: 1, Name: "Taro", Age: 20}
    u2 := User{ID: 1, Name: "Taro", Age: 20}
    if diff := cmp.Diff(u1, u2); diff != "" {
        t.Errorf("u1 and u2 are not equal")
    }
}

実行すると、以下のような結果を得ることができます。

ターミナル
# go test -v
=== RUN   TestStruct
--- PASS: TestStruct (0.00s)
PASS
ok      go-test-practice        0.320s

Diff()のシグネチャ

Diff() のシグネチャは以下の通りです。

compare.go
func Diff(x, y interface{}, opts ...Option) string
  • x, y interface{}
    • 比較対象の2つの値を渡す
    • 型は任意で、空インターフェースなのでどんな型でも受け取れる
  • opts ...Option
    • 可変長引数で、比較の仕方をカスタマイズできる
  • 戻り値 string
    • 比較結果の差分を文字列として返す
    • 空文字列なら一致を意味し、差分がある場合は「-(左側の値), +(右側の値)」の形式で表示

戻り値がユニークなのが特徴です。
テストに失敗すると、以下のような結果を得ることができます。

ターミナル
# go test -v
=== RUN   TestStruct
  main.User{
-       ID:   2,
+       ID:   1,
        Name: "Taro",
        Age:  20,
  }

    main_test.go:21: u1 and u2 are not equal
--- FAIL: TestStruct (0.00s)
FAIL
exit status 1
FAIL    go-test-practice        0.234s

複合型を含む構造体比較

一方で、複合型を含む構造体を比較する場合は、以下のように実装しテストすることができます。

main_test.go
package main

import (
    "testing"
    
    "github.com/google/go-cmp/cmp"
)

type User struct {
    ID int
    Name string
    Age int
    Hobby []string
}

func TestStruct(t *testing.T) {
    u1 := User{ID: 1, Name: "Taro", Age: 20, Hobby: []string{"reading", "running"}}
        u2 := User{ID: 1, Name: "Taro", Age: 20, Hobby: []string{"reading", "running"}}
    if diff := cmp.Diff(u1, u2); diff != "" {
        t.Errorf("u1 and u2 are not equal")
    }
}

実行すると、以下のような結果を得ることができます。

ターミナル
# go test -v
=== RUN   TestStruct
--- PASS: TestStruct (0.00s)
PASS
ok      go-test-practice        0.193s

テストに失敗すると、以下のような結果を得ることができます。

ターミナル
# go test -v
=== RUN   TestStruct
  main.User{
        ID:   1,
        Name: "Taro",
        Age:  20,
        Hobby: []string{
                "reading",
-               "running",
+               "walking",
        },
  }

    main_test.go:22: u1 and u2 are not equal
--- FAIL: TestStruct (0.00s)
FAIL
exit status 1
FAIL    go-test-practice        0.297s

まとめ

今回は「go-cmp」について学びました。

go-cmp は、Goで値や構造体を比較するときに非常に便利なライブラリです。
reflect.DeepEqual() でも比較はできますが、差分が見やすく表示される点や、比較ルールを柔軟にカスタマイズできる点で go-cmp が優れています。

特に、APIレスポンスやDBからの取得結果など、複雑なデータ構造をテストする場面で力を発揮します。
シンプルさと柔軟さを兼ね備えた go-cmp は、Goのテストにおける強力な選択肢のひとつです。

今回で、「Goのテストに入門してみた!」シリーズをおしまいにします!
全19回でテストの基本やさまざまなテクニックを知ることができました!

参考

Discussion