Go ory/dockertestを使ってMongoDBを使うAPIのユニットテスト

### やりたいこと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/4b0daf66-9996-b101-73b9-b50eb0c323ca.png)
APIのエンドポイント1つずつにこんなテストが書いてある。(本来レスポンスも見るしっかりしたテストの方がいいけどそれは一旦置いておく) このテストは、MongoDBのサーバー(Docker Desktop for Windowsのコンテナ)を別途用意した上で、テスト立ち上げの度にそのサーバーのDBを消して初期化し、テストしている。割とよくやる手法なのかもしれないが、この方法だとテストが増えるにつれ、並列処理ができないことから 時間がかかるようになってしまう。できれば並列でできるようにしたいのでその方法を調べてみた。

### 手法いろいろ
#### 1. モックを使う
サーバーがあると仮定して本来返ってくる値を書いておいて返す、的な手法。サーバーに依存しないでテストできて色々楽だと思うが、実際のDBで試したときに想定外のエラー返ってくることもありそうに思い見送り。

#### 2. テスト用のDBサーバーを都度建てる
今回やっていた/やった手法。ここまでやっていたのは、別途DBサーバーを1つだけ建てておいてそれにアクセスさせるテストだったが、[Dockertest](https://github.com/ory/dockertest)というやつを使うと テスト時にユニット毎にコンテナを建ててその中でテストを行い、テスト後はコンテナの削除、というところまでができる。これなら並列でもテストができてよさそう。

#### 3. インメモリなDBを建てて試す
SQL系ならSQLiteで動かすとかができそうに思う。ORMを使っているならワンちゃん、なんとかできるんじゃないだろうか(よく調べられていない) ただ今回はMongoDBなので、SQLiteじゃダメ。[memongo](https://github.com/benweissmann/memongo)という インメモリなMongoDBを建てるものもあったが、サポートOSがUnix系(Linux/MacOS)だったので、Windowsでは動かなそうと思い見送り。

### Dockertestを使ってみた
https://github.com/ory/dockertest/blob/v3/examples/MongoDB.md
基本的にこのExampleを見たらわかる感じなのだが、これはgo-mongodb-driverではなく、mgoを使っていた。go-mongodb-driverでの実装例を載せておく。

#### 実装例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/c5c83bcf-45be-4f3d-2f97-fefa00f6b876.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/54ab55c5-38ce-a7ae-c95f-53ed27d4a1f7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/ec1cda59-f915-7f56-5940-b9e3e66f43b9.png)

##### pool.Runの引数
単に起動するコンテナ内容を指定している。左側がイメージ名、右側がバージョンだった。flagとか使って後から変えれるようにしといたほうがいいかもしれない。

##### defer shutdownの意味
テスト終了時にコンテナを消すやつである。resource.Expire(300)をしておけば5分後に勝手に消滅してくれるはずではあるが、どうも動かないこともあるようだった。

##### No connection could be made because the target machine actively refused it.
で、テストを実行しようとしたらこんなエラーを言われた。その原因は書いてある通りで、Dockerが接続を受け付けてくれなかったことによるもの。 Docker Desktop for Windowsの設定画面を見たらこんな項目があったのでチェックを入れたら動くようになった。ほんとはTLSを使うべきなのだがdockertestのページを見てよくわからなかったので今回は妥協することにした。(やり方分かる人募集)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/fa7c4587-3e8d-cc3f-4e4d-6865e2c894dc.png)

##### Panic: Test timeout
ローカルにイメージが無い初回実行時はダウンロードが終わるまで時間がかかりタイムアウトしてしまう。VSCodeで実行する場合は、settings.jsonのgo.testTimeoutを変更してタイムアウトまでの時間を300秒ぐらいに伸ばしてから再チャレンジ。

##### このままだと並列で動かないが。
テストの関数内上部にt.Parallel() を入れよう。

#### 複雑な実装例
もしかしたらこの方法使うとCIをするときにめんどくさくなるかもしれないので従来の並列じゃないテストも残したくなった。そこで苦戦した結果はこれである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/bd95b5be-3230-7f96-8d7f-c6276c27e9ae.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/b6f186ed-390c-2ce8-605c-fa184666a046.png)

##### flagが反応しない
フラグ名として、parallel って入れてませんか?? parallelは予約語ですよ!!!(2時間悩んだ)

##### テスト用引数の渡し方(VSCode)
ファイル → ユーザー設定 → 設定 → go testって検索 → Go: Test flags → settings.jsonで編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/712121f9-d79b-2116-6d56-0d190eda5380.png)

### 完走した感想
実行までは割とできたけど flagの扱いにめちゃくちゃ悩みました。結局予約語を使っているだけっていう単純なミスで悲しかった。Dockerを見てるとテスト実行時に大量にコンテナが立ち上がって、見てる分には面白いのだけど、あんまり多いと処理落ちしだすするので同時実行数を "-parallel=5" みたいにやって制限しましょう。

### すごく参考にしたページ
GolangでDBアクセスがあるユニットテストのやり方を考える
https://qiita.com/seya/items/582c2bdcca4ad50b03b7