Docker + Travis CI で Codeception CI をしよう

EC-CUBE3 では、PHPUnit + Travis CI を使用し、ユニットテストを自動化しています。
しかし、ここでテストされるのは、単体テストであり、この他、ブラウザを使用しての受け入れテストが必要になってきます。
また、 PHPバージョンを変更してのテストは、[Build Matrix](https://docs.travis-ci.com/user/customizing-the-build#Build-Matrix) で簡単に設定できますが、データベースのバージョンや、ブラウザの種類を変更するのは難易度が高いです。

EC-CUBE3 は比較的こまめにリリースすることで、品質向上を図っていますので、リリースのたびに、多くの環境で受け入れテストをするのは、相応の労力が必要です。ブラウザからのテストといえど、単純なものは自動化してしまいたいですね。

ここで登場するのが [Codeception](http://codeception.com) です。

## Codeception とは？

PHP で書かれたテスティングフレームワークです。

- 受け入れテスト(Acceptance Test)
- 機能テスト(Functional Test)
- 単体テスト(Unit Test)

を、実行することができます。
ここでは、受け入れテストを自動化します。

テストも PHP で記述できます。
Selenium WebDriver を使用して、 Firefox や Google Chrome の受け入れテストが可能です。テストが失敗したら、スクリーンショットも撮ってくれます。

## Codeception の環境構築の難しさ

EC-CUBE3.0 の開発段階から、 Codeception の活用は議論されており、EC-CUBE3.0.6 あたりから、 Codeception による受け入れテスト環境が構築されていました。
当初より、 並列実行のために Docker も使用していたのですが、 IaaS の仮想環境に依存しており、環境構築の難易度も高く、簡単に誰でもテストできるほど自動化できていませんでした。

## Docker Compose を使う

2016年の Docker 関連の進歩は凄まじく、前年とは比べものにならないくらい便利なツールになりました。
EC-CUBE3.0.12 リリースに向けて Codeception の環境整備をしていたところ、 [Docker Compose](http://docs.docker.jp/compose/toc.html) を使用することで、 Acceptance Test も Travis CI で実行して、受け入れテストを完全自動化することができるのではないかと思いつきました。

## Docker Compose で Docker コンテナの依存関係を定義

[EC-CUBE の docker-compose.yml](https://github.com/EC-CUBE/eccube-codeception/blob/master/docker-compose.yml) では、以下のコンテナを定義しています。

| サービス名  | 概要                                                        |
|-------------|-------------------------------------------------------------|
| db          | データベースのコンテナです。 MySQL, PostgreSQL を切り替えることができます。                                  |
| eccube3     | EC-CUBE3が稼動するWebサーバーのコンテナです                 |
| codecept    | Codeception を実行する PHP CLI コンテナです                 |
| browser     | Selenium WebDriver コンテナです。 Firefox, Google Chrome を切り替えることができます。                             |
| mailcatcher | テスティングメールサーバーである MailCatcher のコンテナです |

データベースの種類、バージョンや、ブラウザなどの切替は、  [docker-compose.yml をオーバーライド](http://docs.docker.jp/compose/extends.html#id1) して実行します。


## コンテナ起動途中の待機

docker-compose.yml の `depends_on` で依存関係を定義しますが、実際にコンテナを起動すると、サービスの準備に少し時間がかかる場合があります。
`db` の起動後に `eccube3` を立ち上げるのですが、 デフォルトのままだと、 `db` が完全に起動する前に `eccube3` が起動してしまい、 EC-CUBE のインストールに失敗してしまいます。

これを防ぐため、 [wait-for-pgsql.sh](https://github.com/EC-CUBE/eccube-codeception/blob/master/eccube3/wait-for-pgsql.sh), [wait-for-mysql.sh](https://github.com/EC-CUBE/eccube-codeception/blob/master/eccube3/wait-for-mysql.sh) というシェルスクリプトを用意し、 データベースの起動後に EC-CUBE のインストーラが実行されるよう工夫しています。

同様に [Codeception でも EC-CUBEのインストール完了後にテストを実行するようヘルスチェック](https://github.com/EC-CUBE/eccube-codeception/blob/master/codeception/codecept#L8)しています。

## Codeception の実行

ここまで設定すれば、 以下のワンライナーで Codeception が実行可能です。

```bash
docker-compose run --rm codecept run -d --env default --html report.html
```

PostgreSQL, Selenium/Firefox, EC-CUBE3, Codeception の各コンテナが生成され、テストを実行します。テストレポートは tests/_output 以下へ保存されます。


## Travis CI で完全自動化

ここまで設定すれば、 [Travis CI の Docker サポート](https://docs.travis-ci.com/user/docker/) で完全自動化が可能です！

[.travis.yml](https://github.com/EC-CUBE/eccube-codeception/blob/master/.travis.yml) を設定して、  push すれば受け入れテストを CI できます！


## テストの工夫

テストに様々な工夫をし、安定した継続的インテグレーションを実現しています。

### テストのグループ化

テストケースのクラスやメソッドに [@group アノテーション](http://codeception.com/docs/07-AdvancedUsage#Groups) をつけて、 Codeception の `-g` オプションで指定すると、指定したグループのみ実行できます。
これを利用し、 フロント画面、管理画面3環境の計4環境を並列実行できるようにしています。

### 各種環境でのテスト

[acceptance.suite.yml](https://github.com/EC-CUBE/eccube-codeception/blob/master/tests/acceptance.suite.yml) で、各種環境を定義しています。
ここで設定した内容は、 Codeception の `--env` オプションで指定できます。
`default` は、 Firefox & PostgreSQL です。
`--env chrome,mysql` のように、複数指定することも可能です。

[Codeception MailCatcher Module](https://github.com/captbaritone/codeception-mailcatcher-module) を使用して、 MailCatcher でのテストも統合しています。

[SSL 環境](https://github.com/EC-CUBE/eccube-codeception/commit/3f3c72d31b6945f1cb90932d55c61331f6982e95)や[ファイルダウンロード](https://github.com/EC-CUBE/eccube-codeception/commit/aad260d7347ff5208e3892239ecadd2bda593ef6)のテストもできます。

[サブディレクトリにデプロイした環境でのテスト](https://github.com/EC-CUBE/eccube-codeception#サブディレクトリにデプロイした環境でのテスト実行方法) にも対応しています。

### データベースのバージョン切替

EC-CUBE の下限環境である、 PostgreSQL 8.4.x 及び、 MySQL 5.1.x のテストにも対応しています。
[docker-compose.pgsql84.yml](https://github.com/EC-CUBE/eccube-codeception/blob/master/docker-compose.pgsql84.yml) 及び [docker-compose.mysql51.yml](https://github.com/EC-CUBE/eccube-codeception/blob/master/docker-compose.mysql51.yml) をオーバーライドすることでテスト可能です。

これらのコンテナは、公式 Docker イメージには用意されていないため、[独自にビルド](https://hub.docker.com/r/eccube/)しています。

### EC-CUBEリリースごとの受け入れテスト

基本的に、 master ブランチのテストを対象としています。
[docker build args の ECCUBE_BRANCH](https://github.com/EC-CUBE/eccube-codeception/blob/master/docker-compose.yml#L13) を変更することで、 EC-CUBE3.0.12 以降、任意のタグやブランチで受け入れテスト可能です。

### プラグインのテスト

`-g plugin_installer -g plugin_uninstaller` を[グループに追加](https://github.com/EC-CUBE/eccube-codeception#プラグインをインストールした状態でのテスト実行方法) することでプラグインをインストールした状態でのテストが可能です。インストールするプラグインは  [tests/_data/plugin_fixtures.php](https://github.com/EC-CUBE/eccube-codeception/blob/master/tests/_data/plugin_fixtures.php) で指定可能です。

### 細かなチューニング

JavaScript のイベントで、テストがタイムアウトになったり、失敗しやすいため、 [確実に Drag & Drop](https://github.com/EC-CUBE/eccube-codeception/commit/d1ba4575e52916d3bf268589b0c7324bfc18e018) するようにしたり、 テストに wait を入れるなどして地道にテストの安定性を確保しています。

## その他

応用することで、EC-CUBE2系や、他の Webアプリケーションでも Docker + Travis CI で受け入れテストできると思います。

この受け入れテストは https://github.com/EC-CUBE/eccube-codeception にて公開しています。

## 謝辞

[eccube-codeception にコントリビュートしてくださった方々](https://github.com/EC-CUBE/eccube-codeception/graphs/contributors) 
おかげさまで、とても素晴しい環境を構築することができました。 略式ながら、この場を借りてお礼申し上げます。