CircleCIでのテスト実行時間を短縮する：テストの並列化

Laravelでサービスを提供しており、CircleCI内でテストを実行しています。
テストが増えるに連れてCircleCI内でのテスト時間が気になってきました。

並列のコンテナを増やすのも手ですが、お金もかかるのでとりあえず工夫できることから試してみました。

まずはテストの並列化。

## 現状のCircleCI設定

```yml:.circleci/config.yml
version: 2
references:
  php-c-image: &php-c-image
    image: sencorp/docker-php-c:v2.0.0
  postgres-image: &postgres-image
    image: circleci/postgres:10.1
    environment:
      POSTGRES_DB: hoge
      POSTGRES_USER: fuga
      POSTGRES_PASSWORD: fuga
jobs:
  build:
    docker:
      - *php-c-image
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
              - composer-v2-{{ checksum "laravel/composer.lock" }}
      - run:
          name: composer install
          working_directory: ~/repo/laravel
          command: composer install -n --prefer-dist
      - save_cache:
          key: composer-v2-{{ checksum "laravel/composer.lock" }}
          paths:
            - laravel/vendor
      - restore_cache:
          keys:
              - npm-v2-{{ checksum "laravel/package-lock.json" }}
      - run:
          name: if there is no node_modules, npm ci
          working_directory: ~/repo/laravel
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
      - save_cache:
          key: npm-v2-{{ checksum "laravel/package-lock.json" }}
          paths:
            - laravel/node_modules
  test:
    docker:
      - *php-c-image
      - *postgres-image
    environment:
      APP_ENV: testing
      DB_HOST: localhost
      DB_PORT: 5432
      PAGER: cat # prevent psql commands using less
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
              - composer-v2-{{ checksum "laravel/composer.lock" }}
      - restore_cache:
          keys:
              - npm-v2-{{ checksum "laravel/package-lock.json" }}
      - run:
          name: npm run dev
          working_directory: ~/repo/laravel
          command: npm run dev
      - run:
          name: dump-autoload
          working_directory: ~/repo/laravel
          command: composer dump-autoload
      - run:
          name: db migrate
          working_directory: ~/repo/laravel
          command: php artisan migrate
      - run:
          name: seed data
          working_directory: ~/repo/laravel
          command: php artisan db:seed
      - run:
          name: run test
          working_directory: ~/repo/laravel
          command: ./vendor/bin/phpunit
workflows:
  version: 2
  build-syntax-test:
    jobs:
      - build:
      - test:
          requires:
              - build
```

※不要な部分を削除したり、一部変更してあります

## 並列化にparatestを利用する

phpunitを並列に実行できるparatest。

- [paratestphp/paratest: Parallel testing for PHPUnit](https://github.com/paratestphp/paratest)

### インストール

```sh
$ composer require --dev brianium/paratest
```

### 実行

```sh
$ vendor/bin/paratest --processes 4
```

### 問題発生！

`Storage::fake('my-disk')`が実行毎にディレクトリを初期化してしまう。
並列時には他のテストの部分まで影響してしまう。

解決方法はこちら

- [Laravel Storage and Parallel Testing](https://medium.com/@richartkeil/laravel-storage-and-parallel-testing-5b8eac44a296)

`CreatesFakeStorage`のtraitを作成して、`Storage::fake`を置き換えて解決しました。

## 何並列にするか

こちらは愚直に並列数を増やしながら、時間を計測しました。
今の状態だと10並列が一番速かったので10並列に。

## 並列化したCircleCI設定

```diff:.circleci/config.yml
version: 2
references:
  php-c-image: &php-c-image
    image: sencorp/docker-php-c:v2.0.0
  postgres-image: &postgres-image
    image: circleci/postgres:10.1
    environment:
      POSTGRES_DB: hoge
      POSTGRES_USER: fuga
      POSTGRES_PASSWORD: fuga
jobs:
  build:
    docker:
      - *php-c-image
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
              - composer-v2-{{ checksum "laravel/composer.lock" }}
      - run:
          name: composer install
          working_directory: ~/repo/laravel
          command: composer install -n --prefer-dist
      - save_cache:
          key: composer-v2-{{ checksum "laravel/composer.lock" }}
          paths:
            - laravel/vendor
      - restore_cache:
          keys:
              - npm-v2-{{ checksum "laravel/package-lock.json" }}
      - run:
          name: if there is no node_modules, npm ci
          working_directory: ~/repo/laravel
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
      - save_cache:
          key: npm-v2-{{ checksum "laravel/package-lock.json" }}
          paths:
            - laravel/node_modules
  test:
    docker:
      - *php-c-image
      - *postgres-image
    environment:
      APP_ENV: testing
      DB_HOST: localhost
      DB_PORT: 5432
      PAGER: cat # prevent psql commands using less
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
              - composer-v2-{{ checksum "laravel/composer.lock" }}
      - restore_cache:
          keys:
              - npm-v2-{{ checksum "laravel/package-lock.json" }}
      - run:
          name: npm run dev
          working_directory: ~/repo/laravel
          command: npm run dev
      - run:
          name: dump-autoload
          working_directory: ~/repo/laravel
          command: composer dump-autoload
      - run:
          name: db migrate
          working_directory: ~/repo/laravel
          command: php artisan migrate
      - run:
          name: seed data
          working_directory: ~/repo/laravel
          command: php artisan db:seed
      - run:
          name: run test
          working_directory: ~/repo/laravel
-         command: ./vendor/bin/phpunit
+         command: ./vendor/bin/paratest --processes 10
workflows:
  version: 2
  build-syntax-test:
    jobs:
      - build:
      - test:
          requires:
              - build
```

## 結果

diffの部分だけの計測ですが、実行時間が6分の1まで改善されました！