backend側のコードも増えてきたのでテストコードを追加しました。 backendのテストをする時はfirestoreに接続したくなかったので、回避するためにGoMockを使って実装してみました。
GoMockはテスト用のモックを実装することで、テスト時に呼びたくない処理をモックで実装させることが可能になります。
やれることは、jest.mockに近いですがjsほどダイナミックな処理は書けないので色々と事前準備が必要なので、その書き方について紹介していきます。
まず、以下のコマンドを実行
$ go get github.com/golang/mock/gomock $ go install github.com/golang/mock/mockgen
今回は POST /CreateItem のテストコードを書いていくので、その中でfirestoreを使用している repository/item.go内の処理をMockしていきます。
■ 修正前の「repository/item.go」
Peperomia/item.go at b8497495889324dfd8b66755108c32b9bfb50c2b · wheatandcat/Peperomia · GitHub
まず、GoMockをするのはinterfaceに対してMockする感じになるのでdomainのレイヤーに以下を追加します。
■ domain/item.go
// ItemRepository is repository interface type ItemRepository interface { Create(ctx context.Context, f *firestore.Client, i ItemRecord) error Update(ctx context.Context, f *firestore.Client, i ItemRecord) error Delete(ctx context.Context, f *firestore.Client, i ItemRecord) error FindByUID(ctx context.Context, f *firestore.Client, uid string) ([]ItemRecord, error) DeleteByUID(ctx context.Context, f *firestore.Client, uid string) error }
そして、repository/item.go側に、↑で定義したinterfaceを設定します。
■ repository/item.go
// NewItemRepository is Create new ItemRepository func NewItemRepository() domain.ItemRepository { return &ItemRepository{} }
これで、ItemRepositoryのMockを作成する準備が整ったので、次に以下のコマンドでMockファイルを生成します。
$ mockgen -source domain/item.go -destination domain/mocks/item.go
このコマンドで、↑で定義したinterfaceを元に以下のファイルが生成されます。
https://github.com/wheatandcat/Peperomia/blob/master/backend/domain/mocks/item.go
そして以下のようにテストコードに組み込んでいきます。
■ handler/item_test.go
func TestCreate(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := context.Background() mock := mock_domain.NewMockItemRepository(ctrl) i := domain.ItemRecord{ ID: "sample-uuid-string", UID: "test", Title: "test", Kind: "test", } mock.EXPECT().Create(gomock.Any(), gomock.Any(), i).Return(nil) app := &handler.Application{ ItemRepository: mock, } h := NewTestHandler(ctx, app)
以下で生成したMockで実行したい処理を
mock := mock_domain.NewMockItemRepository(ctrl)
以下で、呼ばれるメソッドの引数と戻り値を事前に設定しておく。(※ gomock.Any()にすると引数は何でもOKになります)
mock.EXPECT().Create(gomock.Any(), gomock.Any(), i).Return(nil)
そしてMockした定義をhandlerのFieldに設定します。
app := &handler.Application{ ItemRepository: mock, } h := NewTestHandler(ctx, app)
そして、handlerのFieldに設定して値を実装の方でも使用することで、実行時はMockしていない処理、テスト時はMockした処理の切り分けを行っています。
■ handler/item.go
// CreateItem 予定を作成する func (h *Handler) CreateItem(gc *gin.Context) { (略) if err := h.App.ItemRepository.Create(ctx, h.FirestoreClient, item); err != nil { NewErrorResponse(err).Render(gc) return }
その他にも、UUID周りのテストでもハマりましたが、そちらはUUIDの部分を参考に対応しました。
最終的には、こんな感じの修正で無事testが通るようになりました
■ 対象pull Request github.com
go testの結果
$ go test -v === RUN TestCreate === RUN TestCreate/ok --- PASS: TestCreate (0.00s) --- PASS: TestCreate/ok (0.00s) PASS ok github.com/wheatandcat/Peperomia/backend/handler 0.439s
これでやっとGo側のテストを書いていけそう