こんにちは!エンジニアの岩間です。
今回は、AWS CDK × CodePipeline × CodeBuildでLaravelのユニットテストを自動化&テストレポート出力してみる機会があったのでその手順とCDKのコードを公開したいと思います。
目次
構成図
構成図は以下です。
開発者がAWS CodeCommitのdevelopブランチにソースコードをプッシュすると、AWS CodePipelineが自動的にトリガーされ、その後AWS CodeBuildでLaravelのユニットテストが行わる、といった流れです。
前提条件
以下のバージョンで構築しています。
AWS CDK:2.116.0
AWS CodePipeline:v1
Laravel:10
Nginx:1.24.0
PostgreSQL:12.14
ディレクトリ構成
ディレクトリ構成は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
. ├── pipeline-cdk(CDKのディレクトリ) ├── docker │ ├── docker-compose.yml │ ├── docker-php │ │ ├── Dockerfile │ │ └── laravel(Laravelソース群) │ ├── docker-nginx │ │ └── Dockerfile │ │ ├── default.conf(Nginxの設定ファイル) │ └── docker-db │ └── init.sql └── buildspec_unit_test.yml(CodeBuildで実行されるファイル) |
コーディング
以下のコードを書いていきます。
- docker-compose.yml(Docker Composeの設定ファイル)
- lib/piepline-cdk-stack.ts(CDKのコード)
- buildspec_unit_test.yml(CodeBuildで実行されるファイル)
- Laravelのコード
docker-compose.yml
まずはdocker-compose.ymlで、CodeBuildで立ち上げる3つのコンテナ(web、app、db)を以下のように定義します。
※sqlファイルはDBコンテナのdocker-entrypoint-initdb.d/配下に配置することでコンテナ初期立ち上げ時に実行してくれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
services: web: # ./docker-nginx内のDockerfileを使用 build: ./docker-nginx container_name: web ports: # ホストのポート80をコンテナのポート80にマッピング - 80:80 depends_on: - app volumes: # Laravelのソース群をコンテナの/var/www/html/laravelにマウント - ./docker-php/laravel:/var/www/html/laravel # Nginxの設定ファイルをコンテナの/var/www/html/laravelにマウント - ./docker-nginx/default.conf:/etc/nginx/conf.d/default.conf platform: linux/arm64 app: # ./docker-php内のDockerfileを使用 build: ./docker-php container_name: app depends_on: - db volumes: # Laravelのソース群をコンテナの/var/www/html/laravelにマウント - ./docker-php/laravel:/var/www/html/laravel platform: linux/arm64 db: container_name: db # イメージ: postgres:12.14 の PostgreSQL データベースを使用 image: postgres:12.14 ports: # ホストのポート5432をコンテナのポート5432にマッピング - 5432:5432 volumes: # sqlファイルをコンテナの/docker-entrypoint-initdb.d/init.sqlにマウント # コンテナ内のdocker-entrypoint-initdb.d/に配置することで、コンテナ初期立ち上げ時に実行してくれます - ./docker-db/init.sql:/docker-entrypoint-initdb.d/init.sql restart: always environment: - POSTGRES_DB=laravel # DB名を指定 - POSTGRES_USER=xxxx # ユーザー名を指定 - POSTGRES_PASSWORD=xxxx # パスワードを指定 platform: linux/arm64 |
各Dockerfileではタイムゾーンや言語の設定を行っています。※今回は省略します
CDK
次にCDKの設定です。今回はTypeScriptで、CI/CDプロセスを自動化するためのCDKスタックを定義しています。
pipeline-cdkディレクトリ配下で、
cdk init sample-app --language typescript
cdk bootstrap
を実行済みの状態で、lib/piepline-cdk-stack.tsに以下の記述を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import { CfnOutput, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as codecommit from 'aws-cdk-lib/aws-codecommit'; import * as codepipeline from 'aws-cdk-lib/aws-codepipeline'; import * as codebuild from 'aws-cdk-lib/aws-codebuild'; import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions'; import * as iam from 'aws-cdk-lib/aws-iam'; export class PipelineCdkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // CodeCommitリポジトリ作成 const sourceRepo = new codecommit.Repository(this, 'test-code-repository', { repositoryName: 'TEST', }); // CodePipeline作成 const pipeline = new codepipeline.Pipeline(this, 'test-pipeline', { pipelineName: 'test-pipeline', }); // CodeBuild作成 const unitTestBuild = new codebuild.PipelineProject( this, 'unit-test', { environment: { // 上記のdocker-compose.ymlのplatformで指定したLinux/ARM64を設定 buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_ARM_3, computeType: codebuild.ComputeType.LARGE, }, environmentVariables: { // DockerHubのユーザーとパスワードを環境変数で渡します ※詳しくはbuildspec_unit_test.ymlで解説 DOCKERHUB_USER: { value: 'xxxxxxxx' }, DOCKERHUB_PASS: { value: 'xxxxxxxx' }, }, buildSpec: codebuild.BuildSpec.fromSourceFilename('buildspec_unit_test.yml'), } ); const sourceOutput = new codepipeline.Artifact(); const unitTestOutput = new codepipeline.Artifact(); // CodePipelineにCodeCommitステージを追加 pipeline.addStage({ stageName: 'Develop-Commit', actions: [ new codepipeline_actions.CodeCommitSourceAction({ actionName: 'CodeCommit', repository: sourceRepo, output: sourceOutput, branch: 'develop', }), ], }); // CodePipelineにCodeBuildステージを追加 pipeline.addStage({ stageName: 'Unit-Testing', actions: [ new codepipeline_actions.CodeBuildAction({ actionName: 'UnitTest', project: unitTestBuild, input: sourceOutput, outputs: [unitTestOutput], }), ], }); // CloudFormation「出力」タブに出力設定 new CfnOutput(this, 'CodeCommitRepositoryUrl', { value: sourceRepo.repositoryCloneUrlHttp, }); } } |
CodeBuildの環境変数で渡しているDockerHubのユーザー名とパスワード(33~39行目)は、cdkディレクトリ内のconfig.tsで環境ごとに定義したり、SSMパラメータストアから取得するやり方がおすすめです。
※DockerHubのユーザーとパスワードの設定については、次のbuildspec_unit_test.ymlのセクションに記載しています。
上記の記述を追加後、cdk deploy
を実行し、正しくリソースが作成されていればOKです。
buildspec_unit_test.yml
buildspecファイルとは、AWS CodeBuildでビルドプロジェクトを実行する際に使用される構成ファイルです。
buildspec_unit_test.ymlを以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
version: 0.2 phases: pre_build: commands: # DockerHubにログイン - echo $DOCKERHUB_PASS | docker login -u $DOCKERHUB_USER --password-stdin # Dockerコンテナ立ち上げ - cd docker && docker-compose up -d build: commands: # appコンテナ内でLaravelのアプリケーションキーを設定 - docker-compose exec app php artisan key:generate --env=testing # appコンテナ内でユニットテスト実行&レポート出力 - docker-compose exec app php artisan test --env=testing --log-junit ./phpunit-log.xml reports: # テスト結果のレポートを定義 SurefireReports: # レポートグループ名を指定 files: # レポートファイルのパスを指定 - 'phpunit-log.xml' # レポートファイルが存在するベースディレクトリを指定 base-directory: 'docker/docker-php/laravel/' # レポートファイルの形式を指定 file-format: JunitXml artifacts: files: # ユニットテストのログファイルをアーティファクトとして保存 - docker/docker-php/laravel/storage/logs/* |
ポイントは以下の3点です。
①DockerHubへのログイン
7行目のコマンド(echo $DOCKERHUB_PASS | docker login -u $DOCKERHUB_USER --password-stdin
)では、DockerHubからのコンテナイメージの呼び出し回数制限エラー回避のため、DockerHubにログインしています。
DockerHubにログインしていない匿名ユーザーの状態だと、 IP アドレス単位での回数呼び出し制限がかかります。
今回、CodeBuild は非 VPC 環境で作成しているため、共用のグローバルIPが利用されることになります。
つまり、CodeBuildから自分がコンテナイメージを呼び出した回数に関係なく、制限に引っかかってしまいます。
※DockerHubにログインしない場合、以下のようなエラーがでることがあります。
toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit
上記のIP制限に引っかからないように、DockerHubのアカウントを作成し、ユーザー名とパスワードでログインをします。DockerHubアカウント作成はこちら
②テストレポート出力引数
appコンテナ内でユニットテスト実行する18行目のコマンド(php artisan test --env=testing
)の引数に--log-junit ./phpunit-log.xml
を指定することで テストレポートの出力に関して指定しています。
PHPUnitの公式ドキュメントに記載されているように、このオプションは、テスト実行の結果をJUnit形式のXMLファイルに出力するために使用されます。
③reportsセクション
20行目のreportsセクションは、AWS CodeBuildのビルドで生成されたレポートを指定するためのセクションです。
このセクションを使用すると、ビルドの結果や生成物を保存し、後で参照できるようにすることができます。
今回、file-formatにはJunitXmlを指定していますが、他の形式も指定可能です。
構文について詳しくはこちら
Laravel設定
最後にLaravel側でユニットテストの設定を行います。
1.テスト用の環境変数ファイルを作成します。
Laravelのプロジェクトディレクトリに移動し、docker/docker-php/laravel/.env.testing
を作成し、以下を編集します。
アプリケーションキーはCodeBuild上で作成するので空欄にします。
1 2 3 4 5 6 7 8 |
APP_ENV=testing APP_KEY= DB_CONNECTION=pgsql DB_HOST=db DB_PORT=5432 DB_DATABASE=laravel DB_USERNAME=docker-compose.ymlで設定したユーザー名 DB_PASSWORD=docker-compose.ymlで設定したパスワード |
2.次にLaravelプロジェクト内のdocker/docker-php/laravel/phpunit.xml
を開き、以下のように変更します。
1 2 3 |
<env name="APP_ENV" value="testing"/> <env name="DB_CONNECTION" value="pgsql"/> <env name="DB_DATABASE"value="laravel"/> |
3.テストケースをdocker/docker-php/laravel/tests
以下に作成します。
動作確認
CodeCommitのdevelopブランチにすべてのソースをpushして、動作確認をします。
AWSコンソールでCodePipelineと検索し、対象のパイプライン名を選択すると以下の画面が確認できます。
CodeCommitのdevelopブランチにpushしたのをトリガーにCodePipelineが動いています!
レポート出力の動作確認
次にレポートが出力されているか確認します。
上記の画面でUnit-Testingステージが成功したら、画面内の「AWS CodeBuild」のリンクをクリックします。
次に、レポートタブをクリックし、レポート名をクリックします。
すると、以下の画像のようにレポート結果が作成されていることが確認できます。
テストに失敗したときは以下の画像のように、不合格の件数と割合も表示されます。
どのテストケースで失敗したのか、失敗時のエラーメッセージ等も確認することができます。
さらに、AWSコンソールのCodeBuild→レポートグループの画面からは、これまで行ったテストのパスレートや実行時間、テスト総数等がレポートグループごとに表示されています。
Artifacts出力の動作確認
ユニットテストのログファイルがアーティファクトとして保存されているか確認します。
Unit-Testingステージが成功した状態で画面内の「AWS CodeBuild」のリンクをクリック→「ビルドの詳細」タブに移動します。
以下の画像のように、アーティファクトのセクションに「アーティファクトのアップロード場所」の項目があるのでリンクをクリックします。
S3の画面が表示されるのでフォルダをダウンロードし、docker/docker-php/laravel/storage/logs/以下のファイルがアップロードされていればOKです。
まとめ
以上、AWS CDK×CodeBuildでLaravelのユニットテストを自動化&テストレポート出力してみるでした。
ここまでお読みいただきありがとうございました。
- 電子工作で自作した植物の自動水やり機から土壌データをAWS IoT Coreに連携してみた - 2024-09-13
- 【ESP8266 NodeMCU】電子工作で植物の自動水やり機を作ってみた - 2024-08-14
- Amazon Bedrock APIでTypeScriptのテストコードを出力してみた - 2024-05-07
- AWS CDK × CodeBuild × CypressでE2Eテストを自動化&実行動画をS3に保存してみる - 2024-04-16
- AWS ECSでブルーグリーンデプロイメント!~デプロイメントタイプ別の動作比較~ - 2024-04-03