gomockのMatcherで構造体を再帰的に比較する

gomockの引数のMatcherでgomock.Eq()を使うときに、構造体がポインタ型だったりするとうまく比較できない。

type Hoge struct {
    ID   string
}

m.EXPECT().Hoge(gomock.Eq(&Hoge{ID: "1"})).Return(nil).AnyTimes() // マッチしない
m.Hoge(&Hoge{ID: "1"})

そのため、go-cmp を使って両者を比較するカスタムMatcherを実装する。

func GomockDeepEq(expected interface{}) gomock.Matcher {
    return &gomockDeepEqMatcher{
        expected: expected,
    }
}

type gomockDeepEqMatcher struct {
    expected interface{}
    diff     string
}

func (m *gomockDeepEqMatcher) Matches(x interface{}) bool {
    m.diff = cmp.Diff(x, m.expected, CmpTransformJSON())
    return m.diff == ""
}

func (m *gomockDeepEqMatcher) String() string {
    return m.diff
}

// interface check
var _ gomock.Matcher = (*gomockDeepEqMatcher)(nil)
type Hoge struct {
    ID   string
}

m.EXPECT().Hoge(GomockDeepEq(&Hoge{ID: "1"})).Return(nil).AnyTimes() // マッチする
m.Hoge(&Hoge{ID: "1"})

ここで、 go-cmp の cmp.Diff() でJSON文字列を比較できるようにする - mrk21::blog {} で述べたように、JSON文字列を比較できるようにしておくとさらに便利になる。

type Hoge struct {
    ID   string
    JSON json.RawMessage
}

m.EXPECT().Hoge(GomockDeepEq(&Hoge{ID: "1", JSON: json.RawMessage(`{"a":1, "b":2}`)})).Return(nil).AnyTimes() // マッチする
m.Hoge(&Hoge{ID: "1", JSON: json.RawMessage(`{"b":2, "a":1}`)})

環境

  • Go: 1.9.3
  • gomock: 1.4.4
  • go-cmp: 0.5.9