背景
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 1.14
- google/mock v1.4.3
対処方法
やることは簡単で、
- インタフェースを含むファイルに
//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
を使うことでモック周りの運用で辛かった部分を解消することができました。