gomock使っていてimport cycle not allowedになってしまったので self_package フラグで対応した

## はじめに

[gomock](https://godoc.org/github.com/golang/mock/gomock)を使ってテストモックを管理する際、self_package というフラグを使うことになりました。このフラグに関して利用方法が書いてある記事がネット上に少なかったので書きます。

## Version

```
$ go version
go version go1.13.1 darwin/amd64
$ cat go.mod | grep mock
        github.com/golang/mock v1.3.1
```

## 構成

`domain.go`が`repositories.go`を利用する構成。repositoriesはDBにあるデータとやり取りするためのインターフェースを持っています。

```
tagservice
├── domain.go
├── domain_test.go
└── repositories.go
```

## やろうとして失敗したこと

自動生成されるモックのファイルを同じ階層に持つのが嫌だったので1階層下にtestmockディレクトリを作ろうとしました。

```terminal:mockgenで生成
$ mockgen -source=repositories.go -destination=./testmock/repositories_mock.go -package=testmock
```

上記を実行すると以下のように`testmock/repositories_mock.go`が作られました。

```
tagservice
├── domain.go
├── domain_test.go
├── repositories.go
└── testmock
    └── repositories_mock.go
```

生成されたモックを元にテストを書いた後、テストを実行してみます。

```terminal:テスト失敗
$ go test
# github.com/momotaro98/mixlunch-service-api/tagservice
import cycle not allowed in test
package github.com/momotaro98/mixlunch-service-api/tagservice (test)
        imports github.com/momotaro98/mixlunch-service-api/tagservice/testmock
        imports github.com/momotaro98/mixlunch-service-api/tagservice
FAIL    github.com/momotaro98/mixlunch-service-api/tagservice [setup failed]
```

`import cycle not allowed in test`と出て失敗してしまいました。

なぜ循環参照になっているかを確認すると生成されたモックのメソッドのシグネチャ内に対象パッケージ(tagservice)で定義されている構造体を含んでいました。

```testmock/repositories_mock.go
// Code generated by MockGen. DO NOT EDIT.
// Source: repositories.go

// Package testmock is a generated GoMock package.
package testmock

import (
	gomock "github.com/golang/mock/gomock"
	tagservice "github.com/momotaro98/mixlunch-service-api/tagservice" // 元のパッケージが必要
	reflect "reflect"
)
/// 中略
func (m *MockITagQueryRepository) QueryTagsWhereTagType(tagTypeId uint8) ([]*tagservice.TagQueryDto, error) {
```

`domain_test.go`が`tagservice`パッケージ内にあるためtagservice->testmock->tagserviceとなっています。

## 対応

選択肢として`domain_test.go`を`repositories_mock.go`と同じ1個下の階層に入れて外部パッケージとしてテストするやり方もありますが、現状都合から難しかったので、生成されるモックファイルをtagserviceパッケージに入れることにしました。

https://github.com/golang/mock/issues/230#issuecomment-446744057 のIssueで`-self_package`フラグが`mogckgen`にあることがわかりました。


#### [-self_package string](https://github.com/golang/mock/blob/0b73a1dbc3e1197079e9b9db31ab2879a5c9a095/mockgen/mockgen.go#L51) (著者日本語訳)
> 生成されるコードが置かれるパッケージimportの絶対パス。 このフラグの目的は、(インターフェースがある)元のパッケージを含めることにより、生成されたコードのインポートサイクルを防ぐことです。 mockgenにてモックのパッケージが入力に設定され、出力がstdioであるため、mockgenは最終出力パッケージを検出できません。 このフラグを設定すると、mockgenが生成するモックファイルにて除外するパッケージimportを指定できます。

#### self_package フラグを使ってモックを生成

```
$ mockgen -self_package=github.com/momotaro98/mixlunch-service-api/tagservice -source=repositories.go -destination=repositories_mock.go -package=tagservice
```

以下のようになりテストが通るようになりました。

```
tagservice
├── domain.go
├── domain_test.go
├── repositories.go
└── repositories_mock.go
```

## ちなみに

mockgenのコマンドは`//go:generate`でコード内に記載して`go generate`で生成できるようにしています。

```domain_test.go
//go:generate mockgen -source=repositories.go -destination=repositories_mock.go -package=tagservice -self_package=github.com/momotaro98/mixlunch-service-api/tagservice
```