
Playwright+Github Actionでビジュアルリグレッションテストしてみた -- 1
シリーズ一覧
- 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

Blog part of ThunderMiracle.com
コメントは表示領域に入ると読み込みます