travis-ciで環境を切り分けたテストをする方法

環境を切り分けたテストがしたい時がある。
具体例を出すと、PHPにはmemcachedのエクステンションとして[Memcache](http://php.net/manual/ja/book.memcache.php)と[Memcached](http://php.net/manual/ja/book.memcached.php)の2つがある。
PHPer以外には冗談みたいな話であるが、本当である。

そして、その2つの環境に対応したパッケージのテストをする場合、2つのエクステンションが個々に入った環境を作らなくてはならないので**面倒くさい**。

面倒くさい事はciにお任せしたいと思ったのでtravis-ciで環境を切り分けたテスト方法を調べて実行してみた。

# 同名env

travis-ciはバージョンごとにテストを実行してくれるのはよく知られていると思うが、
同名の環境変数ごとにもテストを実行してくれる。

つまり

```yaml
php:
  - 5.6
  - 7.0
  - hhvm
  - nightly
env:
  - EXT=none
  - EXT=memcache
  - EXT=memcached
```

だと、バージョン(３つ)×同名環境変数(３つ)で９個のテストが実行される。
あとは、環境変数を使用して環境を作るだけ。

参考:[Environment Variables](https://docs.travis-ci.com/user/environment-variables/)

# 環境変数を使用した環境切り分け

シェルのifで環境変数を見て実行するコマンドを切り分ける

```yaml
before_install:
  - if [[ $EXT = memcache ]]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
  - if [[ $EXT = memcached ]]; then echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
```

上記は`$EXT`が

* memcacheであれば、Memcacheのエクステンションを有効にする
* memcachedであれば、Memcachedのエクステンションを有効にする

という処理を行っている
これで環境構築はできた。

# テストしたくない環境を除外

PHP7はMemcacheエクステンションには対応していないのでMemcacheエクステンションを使用したテストでは実行時に

```
Error: Class 'Memcache' not found
```

と、エラーが出てしまう。

これはパッケージを作った側にはどうしようもないので、PHP７×Memcacheのテストは実行しないようにしたい。
こういう時はmatrixを使う。

```yaml
matrix:
  exclude:
    - php: 7.0
      env: EXT=memcache
    - php: nightly
      env: EXT=memcache
```

上記は

* バージョンが7.0で`$EXT`がmemcacheのテストを除外する
* バージョンがnightlyで`$EXT`がmemcacheのテストを除外する(nightlyも7.0なので)

という処理になる。

matrixのexcludeに除外したい組み合わせを指定するとその組み合わせのテストを実行しないようにしてくれる。
excludeとは逆にincludeの設定もあって、例えば5.6だけMemcacheとMemcachedのテストがしたい場合は

```
php:
  - 5.6
  - 7.0
  - hhvm
  - nightly
env:
  - EXT=none
matrix:
  include:
    - php: 5.6
      env: EXT=memcache
    - php: 5.6
      env: EXT=memcached
```

となる。

参考:[Customizing the Build #Build Matrix](https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix)

# まとめ

* 基本は同名envを設定してenvごとに環境設定を行う
* 実行したくないテストはmatrixのexcludeで除外

PHP以外にも環境を変えたテストを行いたい事はあると思うので覚えておくと楽かも。

# おまけ

この記事のタイトルとは直接関係無いが、
MemcacheとMemcachedに対応させたテストをtravis-ciで実行するときに重要なポイントがあったので記しておく。

## phpunitのグループ機能

PHPはphpunitでテストを実行するのだが、
今回の場合、Memcache用とMemcached用のテストを用意して環境別に実行したかった。
なのでphpunitのグループ機能を活用することにした。

```php
    /**
     * @group memcache
     */
    public function testMemcache()
    {
        // ...
    }

    /**
     * @group memcached
     */
    public function testMemcached()
    {
        // ...
    }
```

上記のようにgroupアノテーションをつけて

```bash
$ vendor/bin/phpunit --group memcache
```

のように実行すればmemcacheグループである`testMemcache()`だけが実行される。

travis-ciで実行する場合はコマンドを環境変数によって切り分ける

```yaml
script:
  - if [[ $EXT = none ]]; then vendor/bin/phpunit --group none; fi
  - if [[ $EXT = memcache ]]; then vendor/bin/phpunit --group memcache; fi
  - if [[ $EXT = memcached ]]; then vendor/bin/phpunit --group memcached; fi
```

あるいは、グループ名と環境変数の中身が同じならば

```yaml
script:
  - vendor/bin/phpunit --group $EXT
```

でも良いかもしれない。

## HHVMのMemcacheエクステンションとMemcachedエクステンション

[HHVM](http://hhvm.com/)の事はあまり詳しくないのだが、HHVMには最初からMemcacheエクステンションとMemcachedエクステンションが入っているようだった。

逆にHHVMで

```yaml
before_install:
  - if [[ $EXT = memcache ]]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
```

とエクステンションを有効にしようとすると、

```
/home/travis/build.sh: line 45: /home/travis/.phpenv/versions/hhvm/etc/php.ini: No such file or directory
```

のように`php.ini`が無いとエラーが出る。
なので、HHVMではエクステンションの有効化をしないようにした。

```yaml
before_install:
  - if [[ $EXT = memcache && $TRAVIS_PHP_VERSION != hhvm ]]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
  - if [[ $EXT = memcached && $TRAVIS_PHP_VERSION != hhvm ]]; then echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
```

# この記事で使用したリポジトリ

[178inaba/travis-php-memcached-test](https://github.com/178inaba/travis-php-memcached-test)