Carpe Diem

備忘録

go generateでモックを生成する

背景

Clean Architecture に則ってレイヤ間をプラガブルにするためにインタフェースを使っていくと、テストのために各インタフェースのモックが欲しくなります。

そのモックを生成する際にgolang/mockを使用するわけですが、毎回

$ mockgen -source user.go -destination mock_user.go

とするのは手間です。また

  • たまにしか使わないので書式、オプションを覚えていない
  • モックの吐き出し先ルールがファイル、パッケージ、リポジトリによって異なり把握がしづらい

など運用が辛くなりがちです。

かと言って「じゃあレイヤまるごと対象にしよう」とシェルスクリプト

for file in $(ls ${SOME_LAYER_DIR} | grep -v "_test.go"); do    
    mockgen -source ${SOME_LAYER_DIR}/${file} -destination mock/${SOME_LAYER_DIR}/${file}   
done

とかすると、

  • あるファイルはインタフェースを含まないので、gomockを使うとimport packageだけの空ファイルができる
    • ↑のせいでビルドやテストがコケる
  • この問題を避けるためにgrep -v除外が増えていく

といったこれまた運用が辛くなりがちです。

今回はgo generateを活用してこの問題を解決します。

環境

対処方法

やることは簡単で、

  • インタフェースを含むファイルに//go:generate mockgen~を書く
  • go generateコマンドでモック生成

の2つだけです。

//go:generate mockgen~を書く

例えば以下のようなインタフェースがあった場合、

package repository

type CardHistory interface {
        Find(fingerprint string) (history model.CardHistory, err error)
        Save(history model.CardHistory) error
}

以下のように//go:generate~を追記します。

//go:generate mockgen -source=$GOFILE -package=mock_$GOPACKAGE -destination=../../mock/$GOPACKAGE/$GOFILE 
package repository

type CardHistory interface {
        Find(fingerprint string) (history model.CardHistory, err error)
        Save(history model.CardHistory) error
}

ポイントは以下です。

  • $GOFILE$GOPACKAGEを使ってファイル名やパッケージ名を統一する
  • destinationはそのファイルを起点とした相対パスが使える

モックファイルはリポジトリのルートにmockを用意して、

  • mock/repository
  • mock/usecase

といった感じにパッケージを切って作ると良いです。
こうしておくと、モックを更新する前にrm -rf mock/*とすれば、元のファイル名を変更したり削除したときでもモックの削除漏れが起きるといったことがなくなります。

以下のような感じですね。

├── domain
│   └── repository
│       └── card_history.go
└── mock
    └── repository
        └── card_history.go

go generateコマンドでモック生成

ではモックファイルを生成します。

$ go generate card_history.go

といった感じで対象のファイルを指定するやり方もできますし、リポジトリのルートから

$ go generate ./...

として//go:generate~が書いてあるファイル全て対象に生成する形もできます。

なので今後モックを生成/再生成したい場合は

$ rm -rf mock/*
$ go generate ./...

とするだけでOKになります。

まとめ

go generateを使うことでモック周りの運用で辛かった部分を解消することができました。

参考