Q. gomockでフィールドの部分一致を検証したい
前提:gomock.Eq について
gomock では、主にgomock.Eq()matcher を利用して値の検証を行える。
次のように直接値を指定したケースでも、内部的にはgomock.Eq()が利用されている。
repo.EXPECT().someMethod(2).Return(nil)このgomock.Eq()では、値の完全一致が求められるため、複雑な構造体を受け取るような場合に全てを列挙するか、gomock.Any()を利用してスタブと化すかのどちらかになってしまう。
// throw error if there are missing or incorrect fieldsrepo.EXPECT().someMethod(chaosObject{ SecondaryField: "value",})gomock.Any()を利用すると値の検証は行われない。
repo.EXPECT().someMethod(gomock.Any())Q. 部分一致で検証したい
ID やタイムスタンプなど動的な値が含まれる場合や、関心事が特定のフィールドのみの場合、部分一致で検証したいことがある。
動的な値に関しては、それを生成する関数をモック化するという手段でも対応できると思われる。
A. Custom matcher を利用する
gomock が提供しているgomock.Matcherを利用して、任意の matcher を作成することができる。
例えば、次のようにcmpを利用して部分一致を検証する matcher を作成することができる。
import ( "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts")
type someObjMatcher struct { expected someObj}
func (m *someObjMatcher) Matches(x interface{}) bool { actual, ok := x.(someObj) if !ok { return false }
return cmp.Equal(actual, m.expected, cmpopts.IgnoreFields(someObj{}, "Field1"))}次のように利用できる。
repo.EXPECT().someMethod(someObjMatcher{ expected: someObj{ Field1: "value", },})次の記事にあるように汎用的な matcher を作成することもできる。
A. Do メソッド内で検証する
gomock.Matcherを利用する方法は、matcher を作成するためのコードが必要になるため、煩雑になることがある。
gomock.Do()内で assetion を行うことでシンプルに記述できる。
repo.EXPECT().someMethod(gomock.Any()).Do(func(arg someObj) { assert.Equal(t, "value", arg.Field1)})ただし、特定のオブジェクトに対する検証が複数箇所で必要になるようなケースでは、Custom matcher の方が適していると思われる。