テストを実行する環境と、テスト用のDBのお話

## これはなんですか
Spring Bootのプロジェクトで実行環境の話のなかで特にテスト環境のお話です。

地球**環境**がどうとかQiitaで探す奇特な人はあまりいないと思いますが、そういう記事ではございません。

## 環境の説明

環境とは。開発物を運用するため、時間、空間などをはじめとしたもろもろのリソースが集約されたものだと定義します。

環境の呼び方については方言があり、実情、ニーズに応じてもろもろが異なります。
私が書いてるこの記事も方言の一種なので、私の書いていることと真っ向から違っていても、『えっ、私の環境おかしすぎ・・？』とはならんやろなのでお気になさらず。

推奨された標準とかはなく、環境は必要に応じて作られていくものだと思います。

私が見てきた中では、だいたい以下のものがあります。

1. 本番環境(production, prod)
    1. いわずとしれた稼ぐところ！
    1. git上ではmainだったりtag打って管理
1. 検証環境(staging, stg)
    1. 本番環境に反映していいか機能の最終的な確認をする環境
    1. gitブランチはmainとかmaster
1. 開発環境(develop, dev)
    1. スプリントの結果などを**みんなで**確認する環境　みんなで確認するためAWSなどのクラウド上に構築されていることが多いです。たまにdockerやらローカルのk8sでこの環境を作り上げているところもあり。
    1. ブランチでいうとdevelop, feat/*
1. ローカル環境(local)
    1. 手元のPC上で実行する環境
    1. ブランチはお好きに
1. ユニット|インテグレーションテストの実行環境(test, UT)
    1. 自動化されたtestが実行される環境
    1. 全てのブランチ

ブランチ戦略はgitflowとgithub flowの併記。

## 設定の切り替えは？

理屈を言えば全ての環境で用いられる資材（dockerイメージやビルドされたアーカイブなどビルドされたもの）は同一で、厳密に言えば**環境ごとにビルドする必要はありません**。

毎回ビルドしているように見えるのはマージされた段階でコードが正しいものかを検証するためである。
運用上環境面とブランチが不可分で結びついてるからそう見えるだけだと思います。

一般的なプログラムのお作法から、資材同一で設定だけを切り替えるのがいいと思っています。

## 異質なテスト環境
[資材は同じだけど設定切り替えについての説明は他に譲る](https://qiita.com/search?q=application.properties)として、上記の中で一つだけ異質な環境があります。

それぞれの環境がmainディレクトリの下のコードを駆動するのに対し、ユニットテストの実行環境はtestのコードから始まります。
厳密に言えば、全ての環境でmainを駆動することには変わりがないのですが、テスト環境以外は main を人間などが扱うのに対し、テスト環境はmainを扱うのがテストコードである点が違います。
そして圧倒的に他の環境よりも短命です。

では、実際にどうなってるのか見ていきましょう。

## Spring Bootのレポジトリとの関係

標準的なプロジェクトでは、下記のようなディレクトリ構成になっています。

[Spring Initializr](https://start.spring.io/) でなにもいじらず Generateした Demo プロジェクトです。

```
$ tree
.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── demo
    │   │               └── DemoApplication.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── com
                └── example
                    └── demo
                        └── DemoApplicationTests.java


```

Spring Bootが動作するための最小構成であり、今回説明したいことの本筋はここにあるため、これを題材にします。

### src/main
何かを実現したいがために書く物が収められている場所。
Controller(API)やService, Repositoryなど、いわゆるProduct(製品)として動作するコードや設定、定義などが収められている。

### src/test
主にmainをテストするためにあるコード。
ビルド時にはこのテストをフルパスすることが前提となる。

### その他
mavenやgradle関連のファイルやREADME.mdとかは必ずあるが今回は説明しない。

## テスト環境の話
まずこの環境は動作する場所をあまり選びません。
開発対象となるバージョンのJDKが動作し、mavenやgradleからインターネットにアクセスできるのであれば、自動的にに環境が構築されて動くことが推奨されます。

これは言語やビルドツールによらない話だと考えます。

だっていちいち自動テストのためになにかするのたるいじゃないですか。
ビルドツールによる自動化は、開発環境整備の中でも最も初歩的で、簡単な自動化のためそこで何ができるのかはお勉強しておいて損はないと思います。

APIKEYなどを外部で作る必要があるなどで、どうしても自動化できない部分はREADME.mdに書いておきましょう。

話を戻します。

UTはmockを使えばだいたい解決するのですが、インテグレーションテストを実行するためにmainのコードが動作する環境を整える必要がある場合、例えばデータベースとか、Redisとか、外部APIの接続先とか、ナントカSDKの設定とか。

これらを用意する必要があります。

あらかじめDBが用意できている環境（たとえばローカル）でテストを実行する場合はそこに繋いでしまえばいいのですが、Github Actionsとか、Jenkinsとかの場合、DBはインスタントに作った方が後腐れがないです。

ですがそれらは、テスト環境でだけ動作すればいいものであるため、テスト実行時に構築、初期データ投入、テストが終わったらお掃除という手順を踏む必要があります。

そのためEmbeddedなデータベースというものを用います。

[Embedded Mysql](https://github.com/wix/wix-embedded-mysql)
[Embedded PostgreSQL](https://github.com/zonkyio/embedded-postgres)

SQLiteあたりなら大した手間もなく構築いけるんですが、RDSという建て付けのものはややめんどくさい手順を踏まなければなりません。

当然これらはテスト環境以外では不要なものであるため、 `src/test` の下に集約する必要があります。