Amazon Web Services ブログ

Selenium, AWS Lambda, AWS Fargate, AWS Developer Tools を使ったサーバーレスなUIテスト

(この記事は、 Serverless UI testing using Selenium, AWS Lambda, AWS Fargate, and AWS Developer Tools を翻訳したものです。)

以前、Using AWS CodePipeline, AWS CodeBuild, and AWS Lambda for Serverless Automated UI Testing (日本語版 ) を公開してから、Chrome headless とFirefox headless が各ブラウザでネイティブにサポートされるようになったことで、事態は大きく変わりました。 AWS Lambda は今やコンテナイメージをサポートし、 AWS Step Functions はLambda と統合された Map state のサポートを追加し、AWS Fargate は完全にサーバーレスのテクノロジを利用した、UIテストを可能にしました。

このブログの目的は、AWS Developer Tools を使ってどのようにテスト環境を自動的にデプロイする継続的デリバリパイプラインを構築し、それに対してUIテストを実行するのかをお見せすることです。また、AWS Lambda や AWS Fargate 上でテストを実行するために利用される、Chromium browserとFirefox browser、Selenium とそれらの依存関係を含んだコンテナを構築する方法も説明します。Step Functions のMap stateを利用することで、複数のテストケースを並行で実行し、テストの速度を加速することができます。

ソリューションの概要

下記の図は私たちのソリューションのアーキテクチャを表しています。私たちにはソースステージとして全てのソースコードが含まれたリポジトリである AWS CodeCommit が設定された、AWS CodePipeline に定義されたパイプラインがあります。ソースが変更されると、このパイプラインが自動的にトリガーされます。続くビルドステージではAWS CodeBuild をつかってDockerfileからコンテナイメージをビルドし、Amazon ECR のリポジトリにイメージをプッシュします。イメージがアップロードされたら、テストステージでは、テストWeb サイトとステータスWebサイトを AWS CloudFormation を使ってAWS Amplify にデプロイします。次のアクションとして、Step Functions を通じてAWS Lambda やAWS Fargate で実行されるテストがトリガーされます。テストステージの完了後、承認のためのEmail がステータスWebサイトのリンクとともに送付されます。承認者がその結果を承認すると、Webサイトは本番環境にデプロイされます。

このソリューションの一部として、UIテストのためのWebサイトと、テスト結果を表示するためのステータスWebサイトを構築し、AWS Amplify でホストするようにしました。

Solutions Design diagram explaining how the solution is structured

ここからはセクションごとに下記を見ていきましょう。

  • コンテナイメージを作成するために利用するDockerfile のステップバイステップガイド
  • 継続的デリバリパイプラインの様々なステージ
  • Step Functions で作られるステートマシンの詳細について
  • Lambda やFargate で実行されたテストの録画方法

Chromium やFirefox ブラウザは、基盤となるオペレーティングシステムで利用可能でアクセス可能であることが期待される特定のライブラリに依存します。 Lambdaのコンテナサポート を使うと依存するパッケージ全てをコンテナの一部としてパッケージするのがとても簡単になります。Chromium は共有メモリを無効化するフラグ (/dev/shm) を提供しているので、Lambda で実行できます。Firefox は fallocate システムコールと /dev/shm に依存しています。この投稿では、Firefox 上のテストを実行するためにFargate を利用します

この投稿で言及するリソースには、GitHub のserverless-ui-testing-using-selenium リポジトリで公開されている AWS CloudFormation のテンプレート、テスト対象とステータスのWebサイト、 AWS CodeBuild のビルド仕様ファイル、Dockerfile、そして、このテストを実行するPythonスクリプト が含まれます。

前提条件

このサンプルのソリューションをデプロイするには、あなたは、下記のサービスへの AWS Identity and Access Management (IAM) アクセスが必要です

また、リポジトリから、clone しpush するためにGit のクライアントが必要です。

コンテナイメージのためのDockerfile の詳細

下記のDockerfile から分かる通り、Lambda とFargateで利用するコンテナイメージをビルドするために、multi-stage ビルドを実行します。プロセスは下記の3つのステージがあります。

  1. lambda-base ステージでは、Python3.8 のAWS base image for Lambda をベースイメージとして、ブラウザや他の依存するソフトウェアをインストールします。このステージでは下記のステップを行います。
    1. あとで使うために、requirements.txtinstall-browsers.sh をコンテナにコピーします。
    2. Chromium とFirefox が必要とする、オペレーティングシステムレベルの依存関係をインストールします。
    3. Selenium WebDriver を含むPython の依存関係をインストールします。
    4. これまでのステップで利用された不要なパッケージを削除します。
  2. ffmpeg ステージではビデオを録画するために必要なFFmpeg をビルドするためのコンテナのベースイメージとして、 AWS base image for LambdaのPython3.8 を使用します。
    1. 作業ディレクトリを設定し、依存するライブラリとFFmpeg をソースからコンパイルするために必要な開発ツールをインストールします。
    2. FFmpegをコンパイルするのに必要な NASM アセンブラをダウンロードしコンパイルします。
    3. FFmpegをコンパイルするのに必要な Yasmアセンブラをダウンロードしコンパイルします。
    4. FFmpeg のソースをダウンロードし、コンパイルします。
  3. 最終ステージでは、最初のステージで構築されたlambda-base イメージを使用し、Lambda とFargate で必要な最終イメージを生成します。
    1. このコンテナの/usr/bin ディレクトリに、先ほどのステージでコンパイルされたFFmpeg バイナリをコピーします。
    2. Lambda のハンドラと、Fargate 用のハンドラを含むアプリケーションコードと、他のテストケースを/var/task ディレクトリにコピーします。

コンテナのエントリーポイントを、Lambda のイメージの設定のオーバーライドとAmazon ECS のタスク定後のコンテナ定義を使って設定します。

# Install Browser, OS dependencies and Python modules
FROM public.ecr.aws/lambda/python:3.8 as lambda-base

COPY requirements.txt /tmp/
COPY install-browsers.sh /tmp/

# Install dependencies
RUN yum install xz atk cups-libs gtk3 libXcomposite alsa-lib tar \
    libXcursor libXdamage libXext libXi libXrandr libXScrnSaver \
    libXtst pango at-spi2-atk libXt xorg-x11-server-Xvfb \
    xorg-x11-xauth dbus-glib dbus-glib-devel unzip bzip2 -y -q

# Install Browsers
RUN /usr/bin/bash /tmp/install-browsers.sh

# Install Python dependencies for function
RUN pip install --upgrade pip -q
RUN pip install -r /tmp/requirements.txt -q

# Remove not needed packages
RUN yum remove xz tar unzip bzip2 -y

# Build ffmpeg
FROM public.ecr.aws/lambda/python:3.8 as ffmpeg
WORKDIR /ffmpeg_sources
RUN yum install autoconf automake bzip2 bzip2-devel cmake libxcb libxcb-devel \
    freetype-devel gcc gcc-c++ git libtool make pkgconfig zlib-devel -y -q

# Compile NASM assembler
RUN curl -OL https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.bz2
RUN tar xjvf nasm-2.15.05.tar.bz2
RUN cd nasm-2.15.05 && sh autogen.sh && \
    ./configure --prefix="/ffmpeg_sources/ffmpeg_build" \
    --bindir="/ffmpeg_sources/bin" && \
    make && make install

# Compile Yasm assembler
RUN curl -OL https://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
RUN tar xzvf yasm-1.3.0.tar.gz
RUN cd yasm-1.3.0 && \
    ./configure --prefix="/ffmpeg_sources/ffmpeg_build" \
    --bindir="/ffmpeg_sources/bin" && \
    make && make install

# Compile FFMpeg
RUN curl -OL https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2
RUN tar xjvf ffmpeg-snapshot.tar.bz2
RUN cd ffmpeg && \
    export PATH="/ffmpeg_sources/bin:$PATH" && \
    export PKG_CONFIG_PATH="/ffmpeg_sources/ffmpeg_build/lib/pkgconfig" && \
    ./configure \
    --prefix="/ffmpeg_sources/ffmpeg_build" \
    --pkg-config-flags="--static" \
    --extra-cflags="-I/ffmpeg_sources/ffmpeg_build/include" \
    --extra-ldflags="-L/ffmpeg_sources/ffmpeg_build/lib" \
    --extra-libs=-lpthread \
    --extra-libs=-lm \
    --enable-libxcb \
    --bindir="/ffmpeg_sources/bin" && \
    make && \
    make install

# Final image with code and dependencies
FROM lambda-base 

# Copy FFMpeg binary
COPY --from=ffmpeg /ffmpeg_sources/bin/ffmpeg /usr/bin/

# Copy function code
COPY app.py /var/task/

継続的デリバリのパイプライン

次にソースコードリポジトリの変更をもとにして自動的にテストと本番環境へのデプロイメントを実施するために、継続的デリバリのパイプラインを作成します。pipeline.yaml テンプレートを使ってAWS CloudFormation でこのパイプラインを作成します。このスタックは、このパイプラインと、それが依存するリソース、そして、モジュールの詳細とテストの結果を保存するためのDynamoDB テーブルを作成します。

下記のスクリーンショットはこのパイプラインです。
Serverless UI Testing Pipeline with multiple stages and actions

このパイプラインは下記のステージを含んでいます。

  • ソースステージ – 設定されたCodeCommit のリポジトリのコードの変更を監視し、CodePipeline のサーバーレスのUI テストのパイプラインをトリガーします。
  • ビルドステージ – CodeBuild でコンテナのイメージをビルドし、CodeBuild の実行 ID を利用してそれを適切にタグづけし、Amazon ECR レジストリにそのイメージをpushします。
  • ビルドステージ – CodeBuild でコンテナのイメージをビルドし、CodeBuild の実行 ID を利用してそれを適切にタグづけし、Amazon ECR レジストリにそのイメージをpushします。
    • 最初のアクションはAWS CloudFormation を使ってLambda 関数、Fargate のタスク、テストの実行をオーケストレーションする、Step Functions のステートマシーンが含まれたテスト環境をデプロイします。
    • テスト環境がデプロイされた後、2つ目のアクションがステートマシーンをトリガーします。このステートマシーンはDynamoDB テーブルをクエリし、実行するべきテストの一覧を取得し、Map stateを使ってこれらのすべてのテストを並行して実行するようにトリガーします。( AWS Management Console かAPI によってパイプラインの一部として作成された、ModulesTable- からはじまるDynamoDB テーブル を更新することで、テストの一覧やテストモジュールを更新することができます)
  • 承認ステージ – テストステージが完了したら、このステージがテスト結果のページへのリンクが記載された通知のためのE-mail を送信します。承認者は、このテストの結果を確認し、本番環境へデプロイを承認、もしくは拒否できます。もし承認者がレビューを拒否した場合、このパイプラインはこのステージで停止します。
  • 本番環境へのデプロイステージ – レビューで承認された場合、パイプラインはアプリケーションを本番環境にデプロイします。

パイプラインが承認ステージに到達した時、(下記のスクリーンショットのような) テストの結果のレビュー用のリンクと、承認、もしくは拒否のためのパイプラインへのダイレクトリンクが記載されたメールが送信されます。

Approval Email with link to review the content and approve or reject it

レビューするコンテンツのリンクを選択すると、テストケースの一覧、テスト結果、ブラウザのバージョン、経過時間などの詳細が表示されるステータスページ(下記のスクリーンショットを見てください)が表示されます。

Test status page with list of executed test cases and its status

Step Functions のステートマシーン

さて、テストをオーケストレーションするStep Functions のステートマシーンを見てみましょう。下記のスクリーンショットを見てください。

Graph of step functions explaining the states in it

このステートマシーンには Task state と、複数の Map state を持つ Parallel state があります。

  • Task state – DynamoDB テーブルは特定のテストモジュールに関連するテストケースの詳細を保持しています。タスクステートでは、テストモジュールをインプットとして受け取り、DynamoDB からテストケースのリストを取得するためのクエリで利用し、特定のテストモジュールに関連するテストケースIDの配列、またはリストを取得します。これらのテストケースはこれら全てを並列で実行する次のステートの入力として渡されます。
  • Parallel state – このステートはChrome 安定版、Chrome ベータ版、Firefox 安定版、Firefox ベータ版の各ブラウザバージョンでテストを並行して実施し、また、他にChrome とFirefoxでビデオとして録画(これは、必要な時にデバッグのために利用できます)するためのテスト実行を起動します。各Parallel stateはTask stateから得られたテストケースの数に応じて、テストケース毎に個別にLambda 関数やFargate タスクをトリガーするMap stateを起動します。実際に実施できる同時実行数のより詳しい情報は Lambda quotasAmazon ECS service quotas をご確認ください。(訳注: StepFunctions のMap state の同時実行数の制約も合わせて考慮する必要があります。)

テスト実行をビデオ録画する

Parallel stateの1つはテスト実行のビデオ録画のためのステートマシンです。テスト実行をビデオとして記録するために、Chrome とFirefoxのヘッドフルブラウザとそれらが起動できるディスプレイが必要です。Lambda とFargate にディスプレイはないので、Xvfb のPython によるラッパーである、 X virtual framebuffer (Xvfb) PyVirtualDisplay を使用します。この例では、Lambda 関数とFargate コンテナでDISPLAY 環境変数の値を:25 に設定しているので、Xvfb は25番のディスプレイでスタートします。FFmpegを使用して、25番のディスプレイ上の入力をx11grab形式で取得し、出力を.mp4の形式でファイルに書き込みます。録画が完了すると、ローカルファイルシステムから、Amazon S3 にアップロードします。ビデオ録画はテストを実行するだけよりもコンピュートリソースと実行時間を必要とするでしょう。そのため、条件によって、ビデオを録画することができます(失敗したテストをデバックのために再実行して録画します)。

GIF of text execution recordeed as video

クリーンアップ

このソリューションの一部として作成されたリソースをクリーンアップするには、下記のリソースを削除してください。

  • SUIT-Prod-Stack- での接頭辞で始まる、CloudFormation スタック
  • pipeline.yaml テンプレートで作成したCloudFormation スタック
  • pipeline スタックによって下記の命名規則で作成された2つのS3 バケット
    • <PIPELINE_STACK_NAME>-codepipeline-artifact-<ACCOUNT_ID>-<REGION>
    • <PIPELINE_STACK_NAME>-test-output-<ACCOUNT_ID>-<REGION>

まとめ

この投稿では、Lambda とFargate 両方で利用できるコンテナイメージをどのようにビルドするのかをお見せしました。私たちは、CodePipeline, CodeBuild, AWS CloudFormation, そしてStep Functionsを使って、UI テストを継続的デリバリのパイプラインに統合しました。ここから、このアプローチをUIテストを超えて拡張し、AWS Developer Tools とサーバーレステクノロジーを利用して、完全に自動化されたテストを統合することができます。

翻訳はソリューションアーキテクトの金森 政雄が担当しました。原文はこちらです。