Goで大量のモックをより統一的に管理し、もっと高速に生成したい!そうだ!!gomockhandlerを使おう!!

インターンとしてメルカリアプリのホーム画面や検索のバックエンド開発に携わっている@sanposhihoです。

この記事ではgolang/mock(以下gomock)のコード生成をgo generateコマンドで行うことに起因する課題点と、それを解決するために作ったgomockhandlerの紹介をします。

https://github.com/sanposhiho/gomockhandler

TL;DR

go generateコマンドによってgomockを利用していると、「大量のモックの生成に時間がかかる」「モックが最新のインターフェースに沿って生成されているか確認できない」などの課題が生じます。 これを解決する、gomockhandlerというツールを作成しました。gomockhandlerを使用することで、多くのモックを一度に高速に生成することやモックが最新であることのチェックを行うことができます。

gomockとは

gomockとはインターフェースの定義からモックの生成を行うGo公式のツールです。

https://github.com/golang/mock

弊社でも利用しているプロジェクトは多くあります。 gomockは以下のようにmockgenコマンドを実行することでモックが生成されます。

mockgen -source=foo.go -destination=../mock/

従来のgo generateを用いたモック管理

前述のようにモックはインタフェース定義を元に生成されます。そのため定義を変更した場合はもう一度同じコマンドでモックを生成し直す必要があります。

また、1つのコマンドにつき1つのモックファイルが生成されるため、数が多い場合に管理が煩雑になっていきます。

そこでモックの生成によく用いられるのがgo generateコマンドです。

//go:generate mockgen -source=foo.go -destination=../mock/

go:generateコメントディレクティブを各インタフェースが定義されているGoファイルに書いておき、go generate ./...を実行することで同時にプロジェクト全体のモックを最新のインタフェース定義をもとに更新できます。

この管理方法は便利ですが、いくつかの問題に直面します。

go generateコマンドでモックを管理することの課題

mockの生成を並列に行うことができない

go generateコマンドはその特性上全てのファイルを走査し//go:generateと書かれたコメントを探し1つずつ実行していくようにデザインされています。 これは実行の順番を保証することで複数のgo:generateコマンドを通して、一連の動作を行うことを可能にするためです。

そのため並列にモックを生成し、実行時間を縮めるということができません。

モックが最新のインタフェースに追従しているかの確認ができない

これによって、

  • 古いインタフェースの定義から生成したモックを使用したテストを書いてしまう
  • 消したはずのインタフェースを元にしてたモックがリポジトリに残ってしまう

という問題が起こります。

古い定義に沿ったモックは不適切なテストを書く原因になってしまうかもしれませんし、消えていてほしいモックが残っているとこれも誤用やプロジェクト理解のコストに余計な負荷がかかってしまうかもしれません。

本来であればCIなどでインターフェースの更新時にモックの更新を行なったかをチェックし、常に最新のモックが置かれている状態を担保したいところです。

モック生成のコマンドの定義が散らばる

モックの生成を行うためのコメントが各ソースコードに散らばります。 プロジェクト全体を通して、どのファイルにgo:generateコメントディレクティブを記載するかやmockgenのオプションの指定などを統一するのは難しいです。

gomockhandlerによる問題の解決

これらの問題を解決するgomockhandlerを開発しました。 https://github.com/sanposhiho/gomockhandler

以下の特徴があります

  • モックの管理を1つの設定ファイルのみを通して行う
  • 並列にモックの生成を行う
  • モックが最新のインタフェースを元に生成されているかを確認できる
  • CLIによって設定ファイルの変更を行う
  • go generateコマンドを使用している既存のプロジェクトでも管理を移行しやすい

1つずつ解説していきます(現時点での最新はv1.1.1です)

モックの管理を1つの設定ファイルのみを通して行う

これによってモックの管理を散らばったコメントの代わりに、一つのファイル内に統一されたフォーマットで行うことができるようになります。 また、ファイル内にすべてのモックの生成方法が記載されているため、他のモックの生成がどのように行われているかを確認しやすくなりモックの生成の方法を統一しやすくなります。

並列にモックの生成を行う

gomockhandlerでは並列にモックの生成を行うのでgo generateと比べて全体のモックの生成が早くなります。

configオプションにてconfigへのパスを指定できます。指定しなかった場合は./gomockhandler.jsonを使用します。

gomockhandler [-config=/path/to/config.json] mockgen

実際に社内のプロジェクトで試してみると

13.1秒→5.4秒(60%減)

1分30秒→27秒(70%減)

このように大幅に時間を短縮することができています。

モックが最新のインタフェースを元に生成されているかを確認できる

gomockhandlerではモックが最新のインタフェースを元に生成されているかを確認できます。

gomockhandler  [-config=/path/to/config.json] check

これによってCIなどでモックが最新の状態になっているかを確認することができます。

CLIによって設定ファイルの変更を行う

設定ファイルに関しては勿論手動でも編集することができますが、CLIによる編集を推奨しています。

  • 生成するモックの登録/更新
  • 生成するモックの削除

がCLIから行えます。

登録や更新にはそのままgomockと全く同じオプションを使用し、mockgenコマンドが正しく実行されるオプションであるかの確認が同時に行われます。

例えば、以下のmockgenコマンドで生成されるmockを登録したい場合

mockgen -source=foo.go -destination=../mock/

以下のgomockhandlerコマンドで設定にモックの登録を行うことができます。

gomockhandler  [-config=/path/to/config.json] -source=foo.go -destination=../mock/

また、生成するモックの削除は以下のように行います。

gomockhandler  [-config=/path/to/config.json] -destination=./mock/user.go deletemock

モックの生成と同様にconfigオプションを指定しなかった場合は./gomockhandler.jsonを使用します。

go generateコマンドを使用している既存のプロジェクトでも管理を移行しやすい

既存のgo generateを使用しているプロジェクトから簡単にgomockhandlerに移行できます。

- //go:generate mockgen -source=$GOFILE -destination=mock_$GOFILE -package=$GOPACKAGE
+ //go:generate gomockhandler -config=/path/to/config.json -source=$GOFILE -destination=mock_$GOFILE -package=$GOPACKAGE

このようにmockgenをそのままgomockhandler -config=/path/to/config.jsonに置き換えて、

go generate ./...

を実行することで全てのmockの設定が一つの設定ファイルに記載されます。これでモックを生成するgo:generateコマンドは必要なくなったのですべて削除しましょう。

また、この際さまざまな位置にファイルがあると思うのでconfigは絶対パスで指定してしまうのが良いと思います。(すぐ消すことになるので)

コメントの置換は最近のエディタであればシュッとできると思います。以下にsedを用いて置換を行う例を示します。

grep -lr "mockgen" ./* | xargs sed -i '' 's/mockgen/gomockhandler -config=\/path\/to\/config/g'

終わりに

この記事ではgomockの管理の辛さを解消するgomockhandlerを紹介しました。現状でgomockに特化したモックの管理ツールというのは他に存在していないように認識しています。 バグ報告やfeature requestなどは是非GitHubで気軽にissueを投げてください。starも頂ければ大きな活力になります🙏

みなさん良いgomockライフを!