Laravel&CircleCIで継続的インテグレーション。ユニットテスト、静的コード解析、E2Eテストなど。
- 公開日
- カテゴリ:Laravel
- タグ:PHP,Laravel,PHPUnit,CircleCI,Github,E2ETesting
Laravelで構築したwebアプリケーションをGithubにてソース管理を行う事も多いですが、CI(continuous integration=継続的インテグレーション)と呼ばれる、ビルドやテストを継続的に行っていきたいところです。
今回はCircleCIとGithubを連携させてLaravelアプリケーションをビルド&テストするまでを見ていきます。
Contents
開発環境
今回の開発環境については以下の通りです。
- PHP 7.3
- MySQL 8.0
- Laravel 5.8
- Circle CI 2.0 - 2.1
laravelアプリケーションのルートディレクトリを laravel/ としています。
GithubとCircleCIの連携
GithubからCircleCIを動作させるには、2つのアカウントを連携させてやる必要があります。
GithubアカウントとCircleCIの連携
CircleCIへアクセスし、Githubアカウントでサインインします。
Github上でCircleCIとの連携承認画面になるので、承認するとアカウントの連携が行われ、CircleCIの管理画面へ遷移します。
任意のリポジトリのCIを開始する
CircleCIの管理画面から「Add Projects」に進むと自身のリポジトリの一覧が表示されるので、そこからCIを回したいリポジトリを選んで(Set Up Project)アクティブにする事で、リモートリポジトリに変更が起こるとCircleCIが動作するようになります。(初めての場合は、サインイン後にリポジトリの一覧が既に表示されたような気もするので、その場合はそこから選択してもOK)
ここまでで、Githubのリモートリポジトリへソースをプッシュした際にCircleCIが動作するようになります。
設定ファイルの作成
CIを回すために、設定ファイルである config.yml を作成します。
今回はアプリケーションソースとCIのファイルを分けて管理するので、以下のようなディレクトリ構成にします。
project_root
├── .circleci
| └── config.yml
|
└── laravel(laravelのソース群)
環境構築の定義
まずは、Laravelアプリケーションを動作させる為の環境構築部分を定義します。
- laravel/.circleci/config.yml
-
version: 2 jobs: build: docker: - image: circleci/php:7.3-stretch-node-browsers - image: circleci/mysql:8.0.17 command: mysqld --default-authentication-plugin=mysql_native_password environment: - APP_DEBUG: true - APP_ENV: testing - APP_KEY: base64:YlIJx6uH3OUb3hxN+PAiJKlC+EGZ2KYi8VHxsfdJpLk= - DB_CONNECTION: circleci working_directory: ~/repo steps: - checkout - run: name: install dockerize command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz environment: DOCKERIZE_VERSION: v0.6.1 - run: name: Install PHP Extensions command: sudo docker-php-ext-install pdo_mysql - restore_cache: keys: - v1-dependencies-{{ checksum "laravel/composer.json" }} - v1-dependencies- - run: name: composer install working_directory: laravel command: composer install -n --prefer-dist - save_cache: paths: - ./vendor key: v1-dependencies-{{ checksum "laravel/composer.json" }} - run: name: Wait for db command: dockerize -wait tcp\://localhost:3306 -timeout 1m - run: name: Migration & Seeding working_directory: laravel command: php artisan migrate --seed
以下、詳細です。
docker:
- image: circleci/php:7.3-stretch-node-browsers
- image: circleci/mysql:8.0.17
command: mysqld --default-authentication-plugin=mysql_native_password
PHP7.3とMySQL8.0のイメージを導入しています。 MySQLはver8.0の為、認証方式を変更しています。
environment:
- APP_DEBUG: true
- APP_ENV: testing
- APP_KEY: base64:YlIJx6uH3OUb3hxN+PAiJKlC+EGZ2KYi8VHxsfdJpLk=
- DB_CONNECTION: circleci
環境変数を定義しています。主にテストを走らせる為に必要な設定を行っています。
以下、runコマンドベースです。
install dockerize MySQLコンテナが立ち上がった事を確認してからマイグレーションやシーディングを実行したいので、そのためのdockerizeのインストールを行っています。(導入は任意) Install PHP Extensions PHP拡張をインストールしています。 composer install Laravelの依存パッケージをインストールしています。 Wait for MySQL MySQLコンテナが立ち上がるまでここでWaitします。 Migration & Seeding マイグレーションとシーディングを行っています。 restore_cache/save_cache はジョブを高速化するためのキャッシュ設定です。詳しくは公式ドキュメントを参照してください。
依存関係のキャッシュ
https://circleci.com/docs/ja/2.0/caching/
DBコネクションの設定
Circle CIでDBを使用するので、専用のコネクション設定をLaravel側に定義しておきます。
- laravel/config/database.php
-
'connections' => [ 'circleci' => [ 'driver' => 'mysql', 'host' => '127.0.0.1', 'port' => '3306', 'database' => 'circle_test', 'username' => 'root', 'password' => '', 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ],
動作確認
ここまでで一旦コミットしてリモートリポジトリにプッシュしてみます。Circle CIが動作するので、管理画面「job」から経過や結果を確認する事ができます。
ステータスが「SUCCESS」になったら成功です。ここまでで、環境の構築が完了しました。
テストの定義
環境構築が出来たので、テストを動作させるように定義していきます。一通りの設定を列挙していますが、それぞれ導入済みの前提で、採用は任意でどうぞ。
ユニットテスト
ユニットテストを走らせる為に、PHPUnitが動作するように定義します。
- laravel/.circleci/config.yml
-
- run: name: Unittest working_directory: laravel command: ./vendor/bin/phpunit
これ以降の記述もそうですが、今回はアプリケーションソースとcircleciの設定ファイルを分けているので、プロジェクトルートから見たアプリケーションルートを working_directory で指定しています。
コーディング規約チェック
PHP_snifferを用いたコーディング規約チェックを走らせます。
- laravel/.circleci/config.yml
-
- run: name: Coding rules check working_directory: laravel command: ./vendor/bin/phpcs --standard=phpcs.xml ./
静的コード解析
Larastanを用いた静的コード解析を走らせます。
- laravel/.circleci/config.yml
-
- run: name: test - Static code analysis working_directory: laravel command: php artisan code:analyse --paths='app,tests' --level=max
E2Eテスト(Dusk)
Laravel Duskでブラウザテストを走らせます。
- laravel/.circleci/config.yml
-
- run: name: Install Chrome Driver working_directory: laravel command: php artisan dusk:chrome-driver 76 - run: name: Start Chrome Driver working_directory: laravel command: ./vendor/laravel/dusk/bin/chromedriver-linux background: true - run: name: Start Laravel Server working_directory: laravel command: php artisan serve background: true - run: name: Run Dusk working_directory: laravel command: php artisan dusk environment: APP_URL: http\://localhost:8000
Install Chrome Driver 任意のバージョンのChromeDriverをインストールしています。ここではver.76をしていしていますが、環境に応じて適切なバージョンを指定してください。 Start Chrome Driver ChromeDriverを起動しています。 Start Laravel Server PHP組み込みWebサーバを起動しています。 test - Dusk Duskでブラウザテストを実行しています。 ## 動作確認
テストの定義を行ったら再度コミットしてリモートリポジトリへプッシュします。CircleCIが動作しテストが行われている事が確認できると思います。
こちらも、ステータスが「SUCCESS」になったら成功です。これで、CircleCIを連携させた自動テストの実行環境が構築できました。
バージョンによる設定ファイル記法の違い
ここまででconfig.ymlの記述については断片的に表示していましたが、バージョンによって若干の違いがある(バージョンアップによる新機能を使わなければ同じ記法では書けます)ので、ここでバージョン別のconfig.ymlを記します。
ver 2.0
これまではこのバージョンで紹介してきました。全体像としては以下になります。
- laravel/.circleci/config.yml
-
version: 2.0 jobs: build: docker: - image: circleci/php:7.3-stretch-node-browsers - image: circleci/mysql:8.0.17 command: mysqld --default-authentication-plugin=mysql_native_password environment: - APP_DEBUG: true - APP_ENV: testing - APP_KEY: base64:YlIJx6uH3OUb3hxN+PAiJKlC+EGZ2KYi8VHxsfdJpLk= - DB_CONNECTION: circleci working_directory: ~/repo steps: - checkout - run: name: install dockerize command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz environment: DOCKERIZE_VERSION: v0.6.1 - run: name: Install PHP Extensions command: sudo docker-php-ext-install pdo_mysql - restore_cache: keys: - v1-dependencies-{{ checksum "laravel/composer.json" }} - v1-dependencies- - run: name: composer install working_directory: laravel command: composer install -n --prefer-dist - save_cache: paths: - ./vendor key: v1-dependencies-{{ checksum "laravel/composer.json" }} - run: name: Wait for db command: dockerize -wait tcp\://localhost:3306 -timeout 1m - run: name: Migration & Seeding working_directory: laravel command: php artisan migrate --seed - run: name: Coding rules check working_directory: laravel command: ./vendor/bin/phpcs --standard=phpcs.xml ./ - run: name: Static code analysis working_directory: laravel command: php artisan code:analyse --paths='app,tests' --level=max - run: name: Unittest working_directory: laravel command: ./vendor/bin/phpunit - run: name: Install Chrome Driver working_directory: laravel command: php artisan dusk:chrome-driver 76 - run: name: Start Chrome Driver working_directory: laravel command: ./vendor/laravel/dusk/bin/chromedriver-linux background: true - run: name: Start Laravel Server working_directory: laravel command: php artisan serve background: true - run: name: Run Dusk working_directory: laravel command: php artisan dusk environment: APP_URL: http\://localhost:8000
var 2.1
本記事執筆時点での最新バージョンです。
- laravel/.circleci/config.yml
-
version: 2.1 executors: default: working_directory: ~/repo docker: - image: circleci/php:7.3-stretch-node-browsers - image: circleci/mysql:8.0.17 command: mysqld --default-authentication-plugin=mysql_native_password environment: - APP_DEBUG: true - APP_ENV: testing - APP_KEY: base64:YlIJx6uH3OUb3hxN+PAiJKlC+EGZ2KYi8VHxsfdJpLk= - DB_CONNECTION: circleci commands: install-dockerize: steps: - run: name: Install dockerize command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz environment: DOCKERIZE_VERSION: v0.6.1 install-php-extensions: steps: - run: name: Install PHP Extensions command: sudo docker-php-ext-install pdo_mysql restore-cache-composer: steps: - restore_cache: keys: - v1-dependencies-{{ checksum "laravel/composer.json" }} - v1-dependencies- composer-install: steps: - run: name: composer install working_directory: laravel command: composer install -n --prefer-dist save-cache-composer: steps: - save_cache: paths: - ./vendor key: v1-dependencies-{{ checksum "laravel/composer.json" }} wait-for-mysql: steps: - run: name: Wait for MySQL command: dockerize -wait tcp\://localhost:3306 -timeout 1m migration-seeding: steps: - run: name: Migration & Seeding working_directory: laravel command: php artisan migrate --seed test-static-code-analysis: steps: - run: name: Coding rules check working_directory: laravel command: ./vendor/bin/phpcs --standard=phpcs.xml ./ - run: name: Static code analysis working_directory: laravel command: php artisan code:analyse --paths='app,tests' --level=max test-unittest: steps: - run: name: Unittest working_directory: laravel command: ./vendor/bin/phpunit test-e2etest: steps: - run: name: Install Chrome Driver working_directory: laravel command: php artisan dusk:chrome-driver 76 - run: name: Start Chrome Driver working_directory: laravel command: ./vendor/laravel/dusk/bin/chromedriver-linux background: true - run: name: Start Laravel Server working_directory: laravel command: php artisan serve background: true - run: name: Run Dusk working_directory: laravel command: php artisan dusk environment: APP_URL: http\://localhost:8000 jobs: build: executor: name: default steps: - checkout - install-dockerize - install-php-extensions - restore-cache-composer - composer-install - save-cache-composer - wait-for-mysql - migration-seeding - test-static-code-analysis - test-unittest - test-e2etest
executors 実行環境を定義する事ができ定義を再利用する事が出来ます。 commands ステップシーケンスを予め定義出来るようになっています。これもexecutors同様、コマンドの再利用が出来て便利です。 見て分かる通り、jobsがとてもシンプルになっています。ワークフローを定義する際にも、2.1ではよりすっきり書くことが出来ます。
- laravel/.circleci/config.yml
-
version: 2.1 executors: default: working_directory: ~/repo docker: - image: circleci/php:7.3-stretch-node-browsers environment: - APP_ENV: testing testing-image: working_directory: ~/repo docker: - image: circleci/php:7.3-stretch-node-browsers - image: circleci/mysql:8.0.17 command: mysqld --default-authentication-plugin=mysql_native_password environment: - APP_DEBUG: true - APP_ENV: testing - APP_KEY: base64:YlIJx6uH3OUb3hxN+PAiJKlC+EGZ2KYi8VHxsfdJpLk= - DB_CONNECTION: circleci commands: install-dockerize: steps: - run: name: Install dockerize command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz environment: DOCKERIZE_VERSION: v0.6.1 install-php-extensions: steps: - run: name: Install PHP Extensions command: sudo docker-php-ext-install pdo_mysql restore-cache-composer: steps: - restore_cache: keys: - v1-dependencies-{{ checksum "laravel/composer.json" }} - v1-dependencies- composer-install: steps: - run: name: composer install working_directory: laravel command: composer install -n --prefer-dist save-cache-composer: steps: - save_cache: paths: - ./vendor key: v1-dependencies-{{ checksum "laravel/composer.json" }} wait-for-mysql: steps: - run: name: Wait for MySQL command: dockerize -wait tcp\://localhost:3306 -timeout 1m migration-seeding: steps: - run: name: Migration & Seeding working_directory: laravel command: php artisan migrate --seed test-static-code-analysis: steps: - run: name: Coding rules check working_directory: laravel command: ./vendor/bin/phpcs --standard=phpcs.xml ./ - run: name: Static code analysis working_directory: laravel command: php artisan code:analyse --paths='app,tests' --level=max test-unittest: steps: - run: name: Unittest working_directory: laravel command: ./vendor/bin/phpunit test-e2etest: steps: - run: name: Install Chrome Driver working_directory: laravel command: php artisan dusk:chrome-driver 76 - run: name: Start Chrome Driver working_directory: laravel command: ./vendor/laravel/dusk/bin/chromedriver-linux background: true - run: name: Start Laravel Server working_directory: laravel command: php artisan serve background: true - run: name: Run Dusk working_directory: laravel command: php artisan dusk environment: APP_URL: http\://localhost:8000 jobs: build: executor: name: default steps: - checkout - install-php-extensions - restore-cache-composer - composer-install - save-cache-composer - run: name: npm install working_directory: laravel command: npm install test: executor: name: testing-image steps: - checkout - install-dockerize - install-php-extensions - restore-cache-composer - composer-install - save-cache-composer - wait-for-mysql - migration-seeding - test-static-code-analysis - test-unittest - test-e2etest workflows: build-and-test: jobs: - build - test: requires: - build
まとめ
Laravelアプリケーションのテストを中心に見ていきました。 config.ymlはこれ以外にも記法があるので、最適な記法を用いてシンプル状態で管理していきたいですね。