2023年3月5日 • ☕️ 7 min read
English

シリーズ一覧

  1. Playwright+Github Actionでビジュアルリグレッションテストしてみた — 1 ← 今はここ
  2. Playwright+Github Actionでビジュアルリグレッションテストしてみた — 2
  3. 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の設定

ドキュメント通りに作りました。

playwright.config.ts
Copy
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-diff.gif

Visualテストを書く

1ページにあたり本当に数行しか書かないですよ。

visual.spec.ts
Copy
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ファイルを生成してくれます。

local-e2e-generated.png

もう一度実行すれば正常終了します。

local-e2e-test-ok.png

Snapshotsを更新したければ、playwright test --update-snapshotsを一度実行するとOKです。

テストはOK、残りはCIで自動的E2Eテストを走らせれば完了です。

CIの整備

ここは自分のプロジェクトを例として紹介します。

  • Next.jsのプロジェクトでVercelで自動デプロイ
  • CIはGithub Actionを使用
  • pnpmを使用

手順はこちらです。

  1. PlaywrightのDockerコンテナーを使用。

    Playwrightを走らせるための環境を用意するのに、一般的にnpx playwright install-depsを実行する必要があります。しかし、このコマンドが非常に重く、Dockerコンテナーを使った方がこのステップを省けるため、テストが早くなります。

  2. プロジェクトのdependenciesをインストール。

  3. VercelのプレビューURLを取得。

    ここはjustincase-jp/vercel-preview-url-alias Github Actionを使います。

  4. 環境変数BASE_URLにプレビューURLを渡す。

  5. テストが失敗した場合問題を特定できるように、playwright-reportフォルダ(テスト結果)をartifactにアップロードする。

少々煩雑なんだけど、まとめるとこうなります。

./.github/workflows/e2e.yaml
Copy
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テストが落ちました。🥲

エラーを確認します。

test-failed-in-ci

なるほど。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の最新版を使用します。

Dockerfile
Copy
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を使いましょう。

docker-compose.yml
Copy
version: '3.8'
services:
  e2e-visual-generator:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - BASE_URL=https://next-startbootstrap-agency.vercel.app/
    volumes:
      - ./e2e:/app/e2e

スナップショット生成

Copy
docker-compose build && docker-compose run

CIで再実験

更新したスナップショットをPRへ反映してテストを走らせると、あれ、また落ちました。Playwrightのレポート確認すると。

VercelのComments Overviewがあるせいで、プレビューモードに1つのボタンが追加されました。その差分でテストが失敗しました。

vercel-comments-overview.gif

原因がわかったら解決方法は難しくありません。E2Eテストの中からスナップショットに入れたくないDOMをとり除ければOKです。

visual.spec.ts
Copy
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

再び修正したテストをプッシュしたら、無事に通りました。よかったです。

残課題

  1. スナップショットの更新は簡単とは言えない

機能の改修、追加した後、手動でdockerを起動してスナップショットを更新するのが、やや面倒です。PRの中で更新したいでしょう。

  1. テスト失敗する時、原因調査の手順が多少煩雑

artifactsをGithub Actionからダウンロード → 解凍 → htmlを開く。

順になるが、理想はPRの中で確認することでしょう。

残課題として今後解決したいと思います。

完了

PlaywrightでWebサイトのE2Eテストを組み込めました。テスト自身がすごいシンプルですが、環境整備は簡単ではありません。慣れるもんかもしれませんが。

では、興味があれば、サンプルプロジェクトをご参考ください。

https://github.com/thundermiracle/next-startbootstrap-agency


関連投稿

Playwright+Github Actionでビジュアルリグレッションテストしてみた -- 2

2023年3月13日

ThunderMiracle

Blog part of ThunderMiracle.com