CodeBuildでLaravelのUnitテストを実行し、レポート機能で結果を見える化してみた
おはようございます。
ベンジャミンの木村と申します!
今回、AWSのCICD機能の一つであるCodeBuildで、LaravelのUnitテストを実行し、その結果を下記のようにグラフで見える化を行いましたので、その方法を紹介させていただきます。
目次
- 構成図
- ディレクトリ構成
- Codeシリーズの紹介
- Unitテストとは?
- 1. CodeBuildでUnitテストを実施してみよう
- 2. CodeBuildでUnitテストの結果をグラフ化しよう
- まとめ
構成図
今回は下記赤枠のCodeBuildでのLaravelのUnitテストを実行しています。
ディレクトリ構成
.
├── Dockerfile (php-fpmコンテナのDockerfile)
├── app (Laravelのアプリディレクトリ) ※Laravelの初期状態をインストールしています。
│ │
│ ├── tests
│ │ ├── Feature
│ │ │ └── ExampleTest.php(Laravelインストール時にあるUnitテスト実行ファイル)
│ │ ├── Unit
│ │ │ └── ExampleTest.php(Laravelインストール時にあるUnitテスト実行ファイル)
│ …………
│
├── appspec.yml
├── builds
│ ├── nginx
│ │ ├── Dockerfile (nginxコンテナのDockerfile)
│ │ └── conf
│ │ └── site.conf
│ └── php
│ ├── conf
│ │ ├── php-extention.ini
│ │ ├── php-fpm.conf
│ │ ├── php.ini
│ │ └── www.conf
│ └── ini
│ ├── php-ext.ini
│ └── xdebug.ini
├── buildspec.yml (CodeBuildの実行ファイル)
├── docker-compose.yml
└── taskdef.json
Codeシリーズの紹介
CodeCommit
ソースコードを管理するAWSのマネージサービスになります。
GithubのAWSバージョンだと考えていただければOKです!
CodeBuild
ソースコードのビルドを行うAWSのマネージドサービスになります。
「ビルドとはなんぞや?」という方は、コードを実際に動かせるように色々変化するツールだと覚えていただければOKです。コンテナを立てる上ではソースコードをDocker imageに変換します。
今回はこのサービスの中でコードのテストを実行します。
CodeDeploy
AWSのマネージドのデプロイサービスになります。
ECSを作成する上ではタスク定義(taskdef.json)というコンテナの設計書をデプロイし、その設計書に沿ってコンテナが立ち上がっていきます。
CodePipeline
CI/CD(継続的デリバリー/継続的インテグレーション)をサポートするサービスになります。
「CI/CDがわからない!」という方は、CodeCommitへソースコードがあがれば、CodeBuildが動いてビルドが完了したら、CodeDeployが動いての一連の動作を繋いであげるためのものと覚えていただければOKです!
Unitテストとは?
Unitテストとは単体テストとも言われます。
ソフトウェア開発において個々の「Unit」(通常はプログラムの中で最小の機能単位)をテストするためのプロセスです。Unitテストの主な目的は、各Unitが設計どおりに動作することを確認することです。
※Unitとは関数・メソッド、クラスやモジュールなどのプログラムの塊のようなものです。
1. CodeBuildでUnitテストを実施してみよう
それでは、CodeBuildでUnitテストを実施していきましょう!
まずは、CodeBuildのビルド実施内容を記載したbuildspec.ymlを用意しましょう。
buildspec.ymlは「nginxコンテナ」と「php-fpmコンテナ」をDocker imageにしてECRに登録するためのコードを記載しています。
赤文字部分でUnitテスト実施するコードを記載しています。Unitテスト実施コードの詳細な内容説明については、この後記載させていただきます。
今回実施するテストコードは、Laravelの初期インストール時に初期から設定されているExampleTest.php
とTestCase.php
を用いて実施します。
version: 0.2
phases:
install:
runtime-version:
docker: 20
pre_build:
commands:
# # ECRリポジトリのURIを環境変数に設定
- REPO_URI_NGINX="${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${ENV}-${SERVICE}-ecr/nginx"
- REPO_URI_PHPFPM="${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${ENV}-${SERVICE}-ecr/php-fpm"
# # タグ名にgitのコミットハッシュを利用
- IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
- TAG="${ENV}-${IMAGE_TAG}"
# # aws cliの設定
- aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com
build:
commands:
# # nginxの設定ファイルの変更
- sed -i -e "s#fastcgi_pass php:9000#fastcgi_pass localhost:9000#" ./builds/nginx/conf/site.conf
# # docker buildを実行
- docker build -f ./builds/nginx/Dockerfile -t src_nginx:latest ./builds/nginx
- docker build -f ./Dockerfile -t src_php:latest .
# # # phpunitの実行
- docker run -d --name test_container -v $(pwd)/app:/prj src_php:latest
- docker exec test_container composer require --dev phpunit/phpunit
- docker exec test_container php artisan test --log-junit phpunit-log.xml
# # doeckr imageのタグ付け
- docker tag src_nginx:latest ${REPO_URI_NGINX}:${ENV}-${IMAGE_TAG}
- docker tag src_php:latest ${REPO_URI_PHPFPM}:${ENV}-${IMAGE_TAG}
- docker images
finally:
# # phpunitの結果を取得
- docker cp test_container:/prj/phpunit-log.xml /tmp/phpunit-log.xml
post_build:
commands:
# # dockr pushを実行
- docker push ${REPO_URI_NGINX}:${ENV}-${IMAGE_TAG}
- docker push ${REPO_URI_PHPFPM}:${ENV}-${IMAGE_TAG}
artifacts:
files:
- appspec.yml
- taskdef.json
それでは追記した内容について、説明させていただきます。
①一時的なコンテナの立ち上げ
コンテナの中でテストを実施するため、一時的なDockerコンテナ(test_container)を起動します。それが下記コマンドになります。
- docker run -d --name test_container -v $(pwd)/app:/prj src_php:latest
各部分の詳細は以下の通りです。
docker run
:Dockerコンテナを起動するコマンドです。-d
:このオプションは、コンテナをバックグラウンドで実行するためのものです。--name test_container
:起動するコンテナにtest_container
という名前を付けています。-v $(pwd)/app:/prj
:ホストマシンの$(pwd)/app
ディレクトリをコンテナ内の/prj
ディレクトリにマウントしています。これにより、ホストマシンとコンテナ間でファイルを共有できます。
※$(pwd)は現在の作業ディレクトリを表します。src_php:latest
:起動するDockerイメージの名前を指定しています。こちらは手前の「# # docker buildを実行
」のところで作成したイメージを使用しています。
このコマンドを実行すると、src_php:latest
イメージを使用して仮のDockerコンテナ(test_container)がバックグラウンドで起動します。そして、ホストマシンの現在の作業ディレクトリのapp
サブディレクトリがコンテナ内の/prj
ディレクトリにマウントされます。
②PHPUnitのインストール
テストコマンドを実施するために、既に実行中のDockerコンテナ(test_container
)内で、Composerを使ってPHPUnitを開発用(dev)としてインストールします。
- docker exec test_container composer require --dev phpunit/phpunit
各部分の詳細は以下の通りです。
docker exec
test_container
:実行中のDockerコンテナ(test_container
)内に入るためのコマンドです。composer require --dev phpunit/phpunit
:Composerを使ってPHPUnitを開発用としてインストールするコマンドです。--dev
オプションは、開発環境専用の依存関係としてパッケージをインストールすることを指定します。
③テストコマンドの実行
既に実行中のDockerコンテナ(test_container
)内で、LaravelのArtisanコマンドラインツールを使ってテストを実行し、その結果をJUnit形式のXMLファイルに出力します。
- docker exec test_container php artisan test --log-junit phpunit-log.xm
各部分の詳細は以下の通りです。
docker exec
test_container
:実行中のDockerコンテナ(test_container
)内に入るためのコマンドです。php artisan test --log-junit phpunit-log.xml
:LaravelのArtisanコマンドラインツールを使ってテストを実行し、その結果をphpunit-log.xml
という名前のJUnit形式のXMLファイルに出力するコマンドです。
※phpunit-log.xml
というファイルはこの後のグラフ化で使用します。
④コンテナ内のXMLファイルをローカル(CodeBuildの/tmpディレクトリ)に出力
コンテナ内で作成したXML(phpunit-log.xml
)ファイルが、コンテナ内に入ったままだと、この後のグラフ化で利用できないため、ローカル(CodeBuildの/tmpディレクトリ)にコピーします。
finally:
# # phpunitの結果を取得
- docker cp test_container:/prj/phpunit-log.xml /tmp/phpunit-log.xml
[finalllyブロックについて]
commandブロックでエラーが発生(テストが失敗)しても、finallyブロック内のコマンドは必ず実行されます。③のphp artisan test
のテストが失敗した際でも、後続のcopyコマンドが実行されるようにfinallyにこのcopyコマンドを置いています。
※コピーファイルがない場合はレポートが「不完全」として表示されます。
2. CodeBuildでUnitテストの結果をグラフ化しよう
項番1で実行したテスト内容を記載したXMLファイルを利用して、CodeBuildのレポート機能でテスト結果をグラフで出力したいと思います。レポート機能の記載については下記コードのAfterで赤色の部分になります。
Before
~~~~~~項番1で記載済みのため省略~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
post_build:
commands:
# # dockr pushを実行
- docker push ${REPO_URI_NGINX}:${ENV}-${IMAGE_TAG}
- docker push ${REPO_URI_PHPFPM}:${ENV}-${IMAGE_TAG}
artifacts:
files:
- appspec.yml
- taskdef.json
After
~~~~~~項番1で記載済みのため省略~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
post_build:
commands:
# # dockr pushを実行
- docker push ${REPO_URI_NGINX}:${ENV}-${IMAGE_TAG}
- docker push ${REPO_URI_PHPFPM}:${ENV}-${IMAGE_TAG}
# # reportのインプット
reports:
SurefireReports:
files:
- 'phpunit-log.xml'
base-directory: '/tmp'
file-format: JunitXml
artifacts:
files:
- appspec.yml
- taskdef.json
各部分の詳細は以下の通りです。
reports
:テストレポートの設定を定義するセクションです。SurefireReports
:テストレポートの名前です。
※この名前は任意で記載できますが、ビルドプロジェクト内で一意である必要があります。files
:テストレポート(phpunit-log.xml
)のファイルを指定します。base-directory
:テストレポートファイルが格納されているディレクトリ(/tmp)を指定します。file-format
:テストレポートファイルの形式(XML)を指定します。
これで準備が整いましたので、コードをCodeCommitにpushしてCICDパイプラインを動かしましょう!
※CodeBuildはテストが失敗すると、ビルドも失敗し、CICDパイプラインが止まります。その場合でもテストレポートは出力されます。
上記の通りCodeパイプラインの操作が完了したら、左ペインの「CodeBuild」→「レポート履歴」→出力された「レポート」をクリックしましょう!
以下のようにテスト結果が表示されれば、完了となります!
※ちなみにレポート出力がうまくいっていないとステータスが「不完全」となります。
余談
初期状態のLaravelをCodeBuildでテストすると一つ失敗します。こちらを無視する方法は、下記階層のphpunit.xml
ファイルの<testsuite name="Feature">
のブロックをコメントアウトしましょう。
├── app
│ ├── phpunit.xml
- コメントアウトする部分
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
そうすることで、失敗したテストが実行されなくなり、下記のようにCodeBuildでのビルドも成功します。
まとめ
いかがでしたでしょうか?
Codebuildのレポート機能を使うと、Unitテストの結果が一目でわかるようになります。
今回はLaravelでUnitテストを実行しましたが、テストコマンドをDjangoやRails用にすることで他のバックエンドでも同じように、テストを実行できます。
他のバックエンドでもコンテナを立ち上げる際にも参考にしていただければ、幸いです。