安全安心の開発体験のために Visual Regression Testing はじめました。

NewsPicks Web Experience Unit でフロントエンドエンジニアをしているじゆんきち(@junkisai)です。

弊チームでは、ここ1年間くらいWeb 版のNewsPicksを新しい基盤に置き換えつつ、見ためも刷新するプロジェクト(以下リニューアルプロジェクト)を進めています。

今回は、弊プロダクトで Visual Regression Testing をはじめて、3ヶ月ほど運用してきたので、今回はその導入や運用にあたっての話をしたいと思います。

導入に至った背景と目的

リニューアルプロジェクトを進めていく中で、コンポーネントやスタイル、デザイントークンの変更や改善が必要になるケースが増えてきたものの、それと同時にコードの規模も大きくなったので、人手で意図しない外観変化を防ぎながらコードの変更をしていくのは至難の業でした。

そこで Visual Regression Testing を導入し、意図しない外観変化をレポートしてもらうことで、安心してコードの変更をしていける状態を目指すことにしました。

使用したツールと構成

ツール

Storybook

コンポーネントをカタログのように管理することができるツールです。

弊プロダクトでは、リニューアルプロジェクト立ち上げ時からコンポーネントを Storybook で管理していたため、UI カタログのストーリー単位で画像をキャプチャして、差分比較します。

GitHub - storybookjs/storybook: Storybook is a frontend workshop for building UI components and pages in isolation. Made for UI development, testing, and documentation.

storycap

Storybook をクロールしてスクリーンショットを撮影するための Storybook アドオンです

GitHub - reg-viz/storycap: A Storybook Addon, Save the screenshot image of your stories via puppeteer.

reg-suit

画像の差分比較を行うためのツールです。 指定のフォルダ内の画像ファイルを S3 や Google Cloud Storage に格納し、派生元(main)と派生先のブランチの画像同士を差分比較した結果をレポートにまとめてくれます。

GitHub - reg-viz/reg-suit: Visual Regression Testing tool

Amazon S3

Storycap でキャプチャされたコンポーネントの画像や、 reg-suit で差分比較した結果をまとめたレポートなどを保存するために使用します

Github Actions

本 Visual Regression Testing は PR 単位でチェックしたいので、Github Actions を使用し CI として組み込みます

構成

以下のような流れで Visual Regression Testing を構築します

Visual Regression Testing の流れ図

  1. git push すると、 Github Actions 上で 2~5 のフローを実行
  2. build-storybook を実行し、Strorybook の HTML を生成
  3. Storycap ですべての Story のスクリーンショットを撮影
  4. reg-suit で派生元のブランチのスクリーンショットを S3 からダウンロードして、3. のスクリーンショットと差分比較
  5. 差分比較結果を PR 内にコメントとして投稿
  6. 実装担当者やレビュワーが意図しない差分が発生していないか確認

導入の流れ

ライブラリのインストール

$ npm install -D reg-suit storycap

storycap の設定

packge.json に screenshot という scripts を追加します。

{
  "scripts": {
    "screenshot": "storycap --serverTimeout 60000 --captureTimeout 10000 --serverCmd 'npx http-server storybook-static --ci -p 6006' http://localhost:6006",
  }
}

上記のコマンドを実行すると、__screenshots__ 下にコンポーネントのスクリーンショットを生成します。

reg-suit の準備

reg-suit のセットアップを行います。

$ npx reg-suit init

上記のコマンドを実行すると、使用するプラグインの選択インタフェースがでるので、以下の3つを選択してインストールします。

  • reg-keygen-git-hash-plugin
    • git のコミットハッシュを使用して、比較元となるスクリーンショットのキーを検出するためのプラグイン
  • reg-notify-github-plugin
    • テスト結果を Github の PR にコメントとして通知するためのプラグイン
  • reg-publish-s3-plugin
    • スクリーンショットや差分比較の結果を Amazon S3 に公開するためのプラグイン
? Plugin(s) to install (bold: recommended) (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◉  reg-keygen-git-hash-plugin : Detect the snapshot key to be compare with using Git hash.
 ◉  reg-notify-github-plugin : Notify reg-suit result to GitHub repository
 ◉  reg-publish-s3-plugin : Fetch and publish snapshot images to AWS S3.
 ◯  reg-notify-chatwork-plugin : Notify reg-suit result to Chatwork channel.
 ◯  reg-notify-github-with-api-plugin : Notify reg-suit result to GHE repository using API
 ◯  reg-notify-gitlab-plugin : Notify reg-suit result to GitLab repository
 ◯  reg-notify-slack-plugin : Notify reg-suit result to Slack channel.
(Move up and down to reveal more choices)

続いて対話型コマンドラインで初期設定に関する質問をされるので、回答していきます

[reg-suit] info Install dependencies to the local directory. This procedure takes some minutes, please wait.
// reg-suit の作業ディレクトリ(default: .reg)
? Working directory of reg-suit. .reg
// .reg を .gitignore に追加するか(default: Yes)
? Append ".reg" entry to your .gitignore file. Yes
// 比較対象の画像を保存するディレクトリ(default: directory_contains_actual_images)
? Directory contains actual images. __screenshots__
// 差分比較のしきい値(default: 0)
? Threshold, ranges from 0 to 1. Smaller value makes the comparison more sensitive. 0.01

[reg-suit] info Set up reg-notify-github-plugin:
// Github PR にコメントをするプラグインを使用するために Github App の client ID を取得するためのWebページを開くか(default: Yes)
? notify-github plugin requires a client ID of reg-suit GitHub app. Open installation window in your browser Yes
// 連携する client ID を入力
? This repositoriy's client ID of reg-suit GitHub app 

[reg-suit] info Set up reg-publish-s3-plugin:
// S3 のバケットを作成するか(default: Yes)
// Amazon S3 のバケットに関する設定は後述する内容で後々行うため、スキップします
? Create a new S3 bucket No
// 既に作成したバケットを使用する場合のバケット名
? Existing bucket name 
// regconfig.json の内容を今回の設定内容に更新するか(default: Yes)
? Update configuration file Yes
// 比較対象の画像保存ディレクトリにサンプル画像を作成するか(default: No)
? Copy sample images to working dir No

Github App の client ID はこちらのURL にて、reg-suit を導入するリポジトリの「Get client ID」ボタンを押下することで取得できます。

reg-viz の Github Appの client ID を取得する画面

以上の設定を完了すると、下記のような regconfig.json という reg-suit に関する設定ファイルが生成されます

{
  "core": {
    "workingDir": ".reg",
    "actualDir": "__screenshots__",
    "thresholdRate": 0.01,
    "ximgdiff": {
      "invocationType": "client"
    }
  },
  "plugins": {
    "reg-keygen-git-hash-plugin": true,
    "reg-notify-github-plugin": {
      "prComment": true,
      "prCommentBehavior": "default",
      "clientId": "XXXXXXXXXXXXXXXXXXXX"
    },
    "reg-publish-s3-plugin": {
      "bucketName": ""
    }
  }
}

Amazon S3 の準備

任意のバケット名で作成します。

このとき差分結果リポートを閲覧できるように、ブロックパブリックアクセスの設定を調整する必要があるため、下記の画像のように設定を変更します。

Amazon S3のバケットのブロックパブリックアクセスの設定変更画面

Github Actions の設定

packge.json に Storybook の起動、画像比較を実行する scripts を追加します。

{
  "scripts": {
    "build-storybook": "build-storybook",
    "screenshot": "storycap --serverTimeout 60000 --captureTimeout 10000 --serverCmd 'npx http-server storybook-static --ci -p 6006' http://localhost:6006",
    "vrt": "reg-suit run"
  }
}

次に、 .github/workflows/vrt.ymlnpm run build-storybook, npm run screenshot, npm run vrt を実行する job を定義します。

name: visual regression testing

on: [push]

jobs:
  vrt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
      - uses: actions/setup-node@v3
      - run: npm install
      - name: workaround for detached HEAD
        run: |
          git checkout ${GITHUB_REF#refs/heads/} || git checkout -b ${GITHUB_REF#refs/heads/}
      - run: npm run build-storybook
      - run: npm run screenshot
      - run: npm run vrt

ここで S3 にアクセスできるように、 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY を設定する必要があります。

以上の工程で PR ごとに Visual Regression Testing を CI で実行する準備が完了です。 CI のジョブが完了すると、下図のような差分比較結果がコメントとして投稿されます。

Visual Regression Testing の実行結果

運用して出てきた課題と解消

Visual Regression Testing を導入することで、導入のモチベーションとしていたコンポーネントやスタイル、デザイントークンのリファクタリングを安心して進めることができるようになりました。

しかし、同時に差分比較のノイズが発生することでレビューに負担がかかったり、意図しない差分に気がつかずに本番に適用してしまうケースがでてきました。具体例をあげると下記のとおりです。

  • 日本語で表示されるはずの部分が□で表示されてしまう
    • 2行以上の文字を省略するコンポーネントなど、□になることで意図しない変更なのかわからない箇所が散見された
  • 画像など外部リソースから取得するデータのロード状況によって、差分比較結果にズレが生じてしまう
  • デフォルトではPC 幅のスクリーンショットの差分比較しかしないため、モバイル幅のみ適用される変更に気がつかない

これらの課題はすべてインターンとして join してくださった hiiragi.tsx さんが 1ヶ月という短い間に解消していただきました!

日本語で表示されるはずの部分が□で表示されてしまっている

.github/workflows/vrt.yml に日本語フォントをインストールするフローを追加し解消していただきました。

name: vrt

on: [push]

jobs:
  vrt:
      # 中略
      - name: install japanese font
        run: |
          sudo apt install fonts-ipafont fonts-ipaexfont
      # 以下略

日本語フォント豆腐問題の解消

画像など外部リソースから取得するデータのロード状況によって、差分比較結果にブレが生じてしまう

こちらは ABEMA 様が同様の問題を Canvas でダミー画像を生成するアプローチを取ることで解消していたいので、参考にさせていただきました。(ref. https://developers.cyberagent.co.jp/blog/archives/29784/

これにより、外部リソースから取得するデータのロードタイミングのズレがなくなり、差分比較結果にブレが出づらくなりました。

Canvas によって生成されたダミー画像で置き換えている様子

デフォルトではPC 幅のスクリーンショットの差分比較しかしないため、モバイル幅のみ適用される変更に気がつかない

こちらは storycap でスクリーンショットを撮影する際の設定(.storybook/preview.js)を追記することで解消しました。

export const parameters = {
  screenshot: {
    viewports: {
      small: {
        width: 350,
        height: 600,
      },
      medium: {
        width: 1025,
        height: 1000,
      },
    },
  },
}

おわりに

Visual Regression Testing を導入することで、開発者が安心してリファクタリングを行えるようになりました。また、3ヶ月ほど運用してきて生じた課題やその解消を行ってきました。しかし、まだ下記のような意図せぬ差分が出たり、テスト環境まわりの問題が残っています。今後もこういった問題の解消を続け、より気楽にリファクタリングを行えるように、Visual Regression Testing を磨き込みたいと思います。

  • 現在の日時を取得するコンポーネントの差分比較結果
  • 動画を扱うコンポーネントの差分比較結果
  • S3 にたまり続けるスクリーンショットと差分比較結果レポート

我々 Web Experience Unit は NewsPicks における Web の価値を届けるべく、より良い技術を模索し、プロダクトを成長させ続けています。 Web フロントエンドエンジニアも積極採用中ですので、少しでも興味のある方は以下からエントリーをお願いいたします!

corp.newspicks.com

参考記事

Page top