Slide 1

Slide 1 text

mockgenによるモック生成を 高速化するツール bulkmockgenのご紹介 Kyoto.go #43 @utgwkk (うたがわきき)

Slide 2

Slide 2 text

自己紹介 @utgwkk (うたがわきき) 株式会社はてな Webアプリケーションエンジニア in 京都 最近はGoを書いて暮らしています

Slide 3

Slide 3 text

みなさん モックしていますか?

Slide 4

Slide 4 text

gomock (mockgen) https://github.com/uber/mock (最近 https://github.com/golang/mock がarchiveされた) mockgenでモックを生成してテストで使う

Slide 5

Slide 5 text

mockgenを使ったモック世界観 (1) // interfaceを定義して type UserStore interface { FindById(ctx context.Context, id string) (*model.User, error) } // モックを生成する //go:generate mockgen -package mock_store -destination mock_store/user_store.go . UserStore

Slide 6

Slide 6 text

mockgenを使ったモック世界観 (2) // モックを注入する ctrl := gomock.NewController(t) m := mock_repo.NewMockUserStore(ctrl) s := NewUserService(s) // モックが呼び出される方法を表明する m.EXPECT().FindById(gomock.Any(), "user"). Return(&model.User{Id: "user"}, nil) // モックを使うメソッドを呼び出してテストする ctx := context.Background() u, err := s.FindUserById(ctx, "user")

Slide 7

Slide 7 text

mockgen便利 モック生成を一手に引き受けてくれる 便利なmatcherがある (gomock.Any(), gomock.InAnyOrder(), …) 呼び出し方が不正だったらテストを落としてくれる

Slide 8

Slide 8 text

mockgenの課題 go generateが直列に実行されるので遅い reflect modeだと都度コンパイルされるので遅い

Slide 9

Slide 9 text

モック生成コマンドが多くなると遅い //go:generate mockgen -package mock_store -destination mock_store/a.go . StoreA //go:generate mockgen -package mock_store -destination mock_store/b.go . StoreB //go:generate mockgen -package mock_store -destination mock_store/c.go . StoreC go generateによるコード生成は直列に実行される Proposal: cmd/go: parallel execution of //go:generate · Issue #20520 · golang/go

Slide 10

Slide 10 text

mockgenのreflect modeの仕組み上遅い モックするinterfaceの情報を得るためにGoのプログラムをコンパイルしている mockgenを実行したらコンパイルが走る!!

Slide 11

Slide 11 text

どんどん遅くなるgo generate 77.70s user 39.76s system 143% cpu 1:21.75 total

Slide 12

Slide 12 text

https://xkcd.com/303/

Slide 13

Slide 13 text

go:generate をまとめることはできるが //go:generate mockgen -package mock_store -destination mock_store/store.go . StoreA,StoreB,StoreC 人間がこの1行を編集しまくる必要がある? うまくコンフリクトを解消できる??

Slide 14

Slide 14 text

bulkmockgen https://github.com/utgwkk/bulkmockgen mockgenのコード生成を1回にまとめて高速化するツールbulkmockgenを作った - 私が 歌川です モック対象のinterfaceをスライスに列挙して一度にコード生成する 移行ツールもある (mockgen-to-bulkmockgen)

Slide 15

Slide 15 text

仕組み モック対象のinterfaceをスライスに列挙する 静的解析 (go/parser, go/ast) でinterfaceのリストを取得する スライスに渡したinterface名を結合してmockgenに渡す

Slide 16

Slide 16 text

デモ 大量のinterface定義に対するモック生成を一括で行う https://github.com/utgwkk/bulkmockgen/tree/main/benchmark/interfaces (カンペ: VSCodeを開いてください)

Slide 17

Slide 17 text

コード生成を速くして効率を上げることに成功 77.70s user 39.76s system 143% cpu 1:21.75 total (before) 52.18s user 19.93s system 209% cpu 34.397 total (after) 関わっているプロジェクトで47秒ほど高速化できた

Slide 18

Slide 18 text

課題 mockgenが生成するコード中のコメントがコンフリクトする!! // Code generated by MockGen. DO NOT EDIT. // Source: example.com/test/repo (interfaces: IFoo,IBar,IBaz…)

Slide 19

Slide 19 text

workaround go generateしたあとにコメントを消す for go_file in `git grep --name-only '^// Code generated by MockGen. DO NOT EDIT.' -- '*.go'`; do perl -i -nlpe '$_="" if m{// Source: example.com/test/repo}' $go_file gofmt -w $go_file done

Slide 20

Slide 20 text

まとめ mockgenによるコード生成をまとめるツールbulkmockgenをご紹介 複数のinterfaceのモックを一度に生成することでコード生成を高速化できた interface一覧を1行にまとめる必要がないので人間に優しい

Slide 21

Slide 21 text

参考 ● mockgenのコード生成を1回にまとめて高速化するツールbulkmockgenを作った - 私が歌川です ● gomockを完全に理解する ● go generateに関するproposal ○ Proposal: cmd/go: parallel execution of //go:generate · Issue #20520 · golang/go ○ proposal: cmd/go: generate allow arguments to span multiple lines · Issue #46050 · golang/go

Slide 22

Slide 22 text

先行研究: gomockhandler Goで大量のモックをより統一的に管理し、もっと高速に生成したい!そうだ!! gomockhandlerを使おう!! | メルカリエンジニアリング mockgenコマンドを並列実行する go generateではなく独自CLIによるモック管理

Slide 23

Slide 23 text

なぜbulkmockgenを作ったのか go generateの仕組みに乗ったまま高速化できないか考えた 既存の仕組みからジャンプが少ないと導入しやすい モックしたいinterfaceをGoのコードとして列挙するのでrenameにも強い

Slide 24

Slide 24 text

構想 gomockhandlerとbulkmockgenを組み合わせることができると爆速でモックを生成でき るのでは??