シリーズ一覧
- Playwright+Github Actionでビジュアルリグレッションテストしてみた — 1 ← 今はここ
- Playwright+Github Actionでビジュアルリグレッションテストしてみた — 2
- Playwright+Github Actionでビジュアルリグレッションテストしてみた — 3
なぜE2Eテストが必要
ライブラリのバージョンアップ、あるいはプロジェクトのリファクタリングをする時、レスポンシブ対応済みのWebサイトのPCサイズ、タブレットサイズ、モバイルサイズ、さらにChrome, Firefox, Safariのマトリックを1つづつチェックするのが負荷が高すぎます。ページ数が増えれば増えるほど、デグレチェックが難しくなり、結局**「バージョンアップするだけで大量な工数がかかるので、後回しにしよう」**と悪循環に陥ります。ホームページなどは会社の看板となり、メンテナンスしないホームページは会社に悪影響を与える、メンテナンスすると膨大な工数、ではどうすればいいでしょうか?
答えは自動テストです。
実際に人間の目で見た目を確認するトイルは、PlaywrightのVisual Comparison機能を活用すれば簡単にできます。
なぜPlaywright
Selenium, Cypressと比べるとまだ若くて実機テストのサポートも不十分だが、一般のWebサイトは実機で確認するまでする必要がないから無視できます。
逆に、Playwrightの1つのテストを書けば、Chrome, Firefox, Safari(Webkit)+各サイズを全部確認できるのでとても便利です。
さらに、PlaywrightはJavaScriptだけではなく、.Net, Python, Javaなどもサポートしているので、将来は有望だと思われます。
詳細の比較について、こちらご覧ください。 https://www.lambdatest.com/blog/playwright-vs-selenium-vs-cypress/
PlaywrightでVisualテストする
ドキュメントの通りで、Playwright側はとても簡単です。https://playwright.dev/docs/test-snapshots
Playwrightの設定
ドキュメント通りに作りました。
import { devices, defineConfig } from '@playwright/test';
export default defineConfig({
workers: process.env.CI ? '100%' : undefined,
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], locale: 'ja-JP' },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'], locale: 'ja-JP' },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'], locale: 'ja-JP' },
},
{
name: 'chromium mobile',
use: { ...devices['Galaxy S9+'], locale: 'ja-JP' },
},
{
name: 'webkit mobile',
use: { ...devices['iPhone 13'], locale: 'ja-JP' },
},
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000', },
reporter: 'html',});
28行目に注目しましょう。環境変数process.env.BASE_URL
を使う理由はCI実行中のbaseURLが変わるからです。
30行目:htmlのレポーターに、かなり使いやすいビフォーアフターのコンペアUIがあります。
Visualテストを書く
1ページにあたり本当に数行しか書かないですよ。
import { test, expect } from '@playwright/test';
test('visual landing page test', async ({ page }) => {
await page.goto('');
await expect(page).toHaveScreenshot({ fullPage: true });
});
ローカルで実行(playwright test
)してみたら、初回目が失敗するが、比較用のスナップショット、pngファイルを生成してくれます。
もう一度実行すれば正常終了します。
Snapshotsを更新したければ、playwright test --update-snapshots
を一度実行するとOKです。
テストはOK、残りはCIで自動的E2Eテストを走らせれば完了です。
CIの整備
ここは自分のプロジェクトを例として紹介します。
- Next.jsのプロジェクトでVercelで自動デプロイ
- CIはGithub Actionを使用
- pnpmを使用
手順はこちらです。
-
PlaywrightのDockerコンテナーを使用。
Playwrightを走らせるための環境を用意するのに、一般的に
npx playwright install-deps
を実行する必要があります。しかし、このコマンドが非常に重く、Dockerコンテナーを使った方がこのステップを省けるため、テストが早くなります。 -
プロジェクトのdependenciesをインストール。
-
VercelのプレビューURLを取得。
ここはjustincase-jp/vercel-preview-url-alias Github Actionを使います。
-
環境変数
BASE_URL
にプレビューURLを渡す。 -
テストが失敗した場合問題を特定できるように、playwright-reportフォルダ(テスト結果)をartifactにアップロードする。
少々煩雑なんだけど、まとめるとこうなります。
name: e2e
on: [deployment_status]
jobs:
visual-tests:
name: "basic visual comparison tests"
timeout-minutes: 5
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.31.2-focal
steps:
- uses: actions/checkout@v3
- run: corepack enable
- uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --ignore-scripts
- name: Get Vercel's Alias Preview URL
id: alias-preview-url
uses: justincase-jp/vercel-preview-url-alias@0.2.1
with:
vercel_access_token: ${{ secrets.VERCEL_ACCESS_TOKEN }}
vercel_project_id: ${{ secrets.VERCEL_PROJECT_ID }}
fail_when_cancelled: false
- name: Tests
if: steps.alias-preview-url.outputs.status == 'READY'
run: HOME=/root pnpm e2e env:
BASE_URL: https://${{ steps.alias-preview-url.outputs.preview_url_origin }}
- name: Upload HTML report as artifact.
uses: actions/upload-artifact@v3
if: always()
with:
name: visual-tests
path: playwright-report
33行目:唯一注意して欲しいのはテストの実行する部分です。Dockerコンテナーの中でpnpmコマンドを実行しているため、HOME/root
をつける必要があります。
早速実験
PRを出して試したら、E2Eテストが落ちました。🥲
エラーを確認します。
なるほど。Playwrightで生成してくれたスナップショットのファイル名はvisual-landing-page-test-1-chromium-darwin.png
でした。
つまり、スナップショットのファイル名の命名ルールは[testの説明][projectのname]-[OS名].png
となります。darwinはMacOSの中で生成されたスナップショットのサフィックスとなります。PlaywrightのDockerコンテナーならUbuntuなので、サフィックスがlinuxとなります。だから、確認用のスナップショットが見つからずテストが落ちました。
では、なぜそういうサフィックスを勝手につけてくれるのでしょうか?
理由はOS+ブラウザー+サイズ
により、レンダリングする方法が異なるからなのです。
解決方法は同じ環境、いわゆるPlaywrightのDockerコンテナー中からテスト用のスナップショットを生成することでしょう。
Playwright Dockerコンテナーでスナップショットを生成
実現するために、方法はいくつあります。理想的にはPRの中、何らかのトリガーでスナップショットを生成(あるいは更新)すれば一番ですが、設定は簡単とは言えません。
では、とりあえず一番シンプルな方法でやってみたいと思います。それはローカルでDockerコンテナーを立ち上げて、スナップショットを生成することです。
Dockerfileの準備
DockerイメージはCIと同じにする必要があります。Playwrightの最新版を使用します。
FROM mcr.microsoft.com/playwright:v1.31.2-focal
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install
COPY . .
CMD [ "pnpm", "e2e:upd" ]
docker-composeの設定
生成されたスナップショットを同期する必要があるため、/e2e/visual.spec.ts-snapshots
フォルダをマウントする必要があります。dockerコマンド毎回打つのが面倒なので、docker-composeを使いましょう。
version: '3.8'
services:
e2e-visual-generator:
build:
context: .
dockerfile: Dockerfile
environment:
- BASE_URL=https://next-startbootstrap-agency.vercel.app/
volumes:
- ./e2e:/app/e2e
スナップショット生成
docker-compose build && docker-compose run
CIで再実験
更新したスナップショットをPRへ反映してテストを走らせると、あれ、また落ちました。Playwrightのレポート確認すると。
VercelのComments Overviewがあるせいで、プレビューモードに1つのボタンが追加されました。その差分でテストが失敗しました。
原因がわかったら解決方法は難しくありません。E2Eテストの中からスナップショットに入れたくないDOMをとり除ければOKです。
import { test, expect } from '@playwright/test';
test('visual landing page test', async ({ page }) => {
await page.goto('');
// remove vercel live comments part const vercelPreviewComments = await page.$('vercel-live-feedback'); if (vercelPreviewComments) { await vercelPreviewComments.evaluate((node) => node.remove()); } await expect(page).toHaveScreenshot({ fullPage: true });
});
<vercel-live-feedback />
タグを全部削除することです。
CIで再実験No.2
再び修正したテストをプッシュしたら、無事に通りました。よかったです。
残課題
- スナップショットの更新は簡単とは言えない
機能の改修、追加した後、手動でdockerを起動してスナップショットを更新するのが、やや面倒です。PRの中で更新したいでしょう。
- テスト失敗する時、原因調査の手順が多少煩雑
artifactsをGithub Actionからダウンロード → 解凍 → htmlを開く。
順になるが、理想はPRの中で確認することでしょう。
残課題として今後解決したいと思います。
完了
PlaywrightでWebサイトのE2Eテストを組み込めました。テスト自身がすごいシンプルですが、環境整備は簡単ではありません。慣れるもんかもしれませんが。
では、興味があれば、サンプルプロジェクトをご参考ください。
https://github.com/thundermiracle/next-startbootstrap-agency