Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

Visual Regression Testingで安心できるフロントエンド環境を作る

こんにちは。Eight事業部で主にフロントエンドを担当している青山です。
今回はEightのWebフロントエンドコンポーネント集にVisual Regression Testingを導入した事例を紹介します。 他社さんの事例や勉強会を見るに敷居も下がってきているようで、遅ればせながらDX(開発者体験)向上を見据えて環境を構築していきました。

Visual Regression Testing とは

Visual Regression Testing (以下、VRT) は日本語で画像回帰テストと呼ばれています。対象の修正前後の画像を比較し、差分がないこと、もしくは差分が正しいことをチェックします。 GUIアプリケーションの場合、最終的にユーザーが触れるのは画面であり、この状態をスナップショットでチェックできるのはとても安心できるものだと思います。

Eightのコンポーネント集

今回導入対象として目をつけたのがEight内のコンポーネント集です。その内容について簡単にご紹介します。
EightではWebフロントエンドの構築にReactを利用しています。 Reactは自体はとても使いやすいUIライブラリですが、決まりごとがない状態でチーム開発を進めていくとカオスになっていきます。 特に、似たようなスタイルの乱立や同じスタイルでもそれぞれでハードコーディングされているなど、サービスの統一的なデザインの障害になってきます。 そこでスタイルガイド*1の一部としてReact用のコンポーネント集を準備しました。経緯などについては以前、弊社のイベントで少しご紹介しています。

コンポーネント集は以下の技術スタックで作られており、GitHub上で管理されています。

  • React
  • TypeScript
  • CSS Modules
  • Storybook
  • Jest

StorybookはReactをはじめとしたUIコンポーネントをカタログ化して確認できるツールです。

storybook.js.org

コンポーネント集ではStorybookのstoryとしてコンポーネントの状態ごとにUIを確認できるようになっています。 GitHub Actionsによるワークフローを組んでおり、CI時に静的ページとしてS3にデプロイしています。 さらにその流れの中でPR上に確認用のURLがコメントされるようにしており、「デモ画面」を見れば誰でも変更内容を触って確認できます。

f:id:mt-blue81:20210314140756p:plain
GitHub Actionsでのワークフロー

f:id:mt-blue81:20210302163150p:plain
デプロイ先のURLをコメント

VRTの導入

すでにJestによるDOMの差分チェックは行っていましたが、レビューなどで常に目視確認するのは少し無理があるように思います。 そこで先にも述べたようなメリットを享受できるVRTの導入を進めることにしました。
Storybookの準備が整っていたこともあり、Storycap + reg-suit という組み合わせでのVRT環境としました。

Storycap

StorycapはStorybookのstoryごとの画面キャプチャを保存してくれるStorybookのアドオンです。

github.com

サイト内のManaged modeに記載のとおり、以下の手順のみで準備完了です。

  • yarn add -D storycap
  • stroybookのaddonにstorycapを追加
  • storybookの設定にdecorator (withScreenshot) を追加

reg-suit

reg-suitは画像の比較を行なうためのVRT用ツールで、今回はStorycapの出力を利用します。

github.com

reg-suitのプラグイン機能を利用すると、以下のようなことを一括でやってくれるのでとても便利です。

こちらもStorycapと同様にサイト内のGetting Startedに従って、インストールと設定をしていきます。

  • npm install -g reg-suit
  • reg-suit init --useYarn

質問に対話形式で答えていくことで設定ファイルが出来上がります。 以下が今回作成された設定ファイル regconfig.json です。

{
  "core": {
    "workingDir": ".reg",
    "actualDir": "__screenshots__",
    "thresholdRate": 0.001,
    "ximgdiff": {
      "invocationType": "client"
    }
  },
  "plugins": {
    "reg-keygen-git-hash-plugin": {},
    "reg-notify-github-plugin": {
      "prComment": true,
      "prCommentBehavior": "default",
      "setCommitStatus": false,
      "clientId": "{GitHub AppクライアントID}"
    },
    "reg-publish-s3-plugin": {
      "bucketName": "{バケット名}",
      "pathPrefix": "{バケット内パス}",
      "acl": "private"
    }
  }
}

reg-notify-github-pluginはGitHub Appになっており、 上記コマンドの中でインストールやクライアントIDの取得を行います。 今回は上記設定でCIステータスへの反映を止めています。PRでの不要な混乱を避けるためでしたが、renovateによる自動マージを実施している部分などは反映しても良さそうだと感じています。

ワークフロー

今回はCIワークフローとしてGitHub Actionsを利用しています。
CIサービスの設定方法も各種揃っているので参考にして設定していきます。 Eightではすでにワークフローでstorybookのサイトを作成してあったので、既存の設定に以下を追記しました。*2

- name: add JP font
  run: |
    sudo apt-get install fonts-ipafont-gothic fonts-ipafont-mincho
- name: workaround for detached HEAD
  run: |
    git checkout ${GITHUB_REF#refs/heads/} || git checkout -b ${GITHUB_REF#refs/heads/} && git pull
- name: capture snapshots
  run: |
    yarn storycap http://127.0.0.1:8080 --serverCmd "npx http-server storybook-static"
- name: run reg-suit
  run: |
    yarn reg-suit run

これで、これまでのワークフローに画像差分確認が追加され、以下のようになります。

f:id:mt-blue81:20210314140313p:plain
画像差分確認を追加

できたもの

GitHub ActionsでPRに以下のようなコメントが差し込まれるようになります。 青い丸が差分がないもの、赤い丸が差分が生じたものを示します。 今回意図的にアイコンを入れ替えたPRを作成してみました。

f:id:mt-blue81:20210314192240p:plain

レポートのHTMLで以下のようなサマリと画像比較ページを確認できます。 お見せしている画像はblendモードで変更前後を一枚の画像で表現していますが、その他にも変更前後を横並びで表示する2upモードなど、reg-suitからとても見やすいレポートが提供されます。

f:id:mt-blue81:20210314192641p:plain
画像比較レポートページ
f:id:mt-blue81:20210314192702p:plain
レポート詳細ページ

まとめ

EightでのStorybookによるVRTの導入事例についてご紹介しました。

素晴らしいOSSを組み合わせることで、準備次第では本当にサクッと導入できるものになっているんだという実感があります。 今回の例はコンポーネント集というシンプルなものへの適用ですが、サービス本体にも適用できるようになれば、スタイル変更の影響などが明確になり、恐る恐るCSSを変更することが減るでしょう。
これからVRT導入することを検討されている方への助けになれば幸いです。


buildersbox.corp-sansan.com buildersbox.corp-sansan.com

*1:スタイルガイドの範疇についてはこの記事では言及しませんが、コンポーネント集以外にデザイン原則やFigmaによるデザイン方針の言語化も行っています。

*2:当初pull_requestをトリガーにしようと考えましたが 、マージの際に比較データを作れないためマニュアルどおりpushをトリガーにしています。

© Sansan, Inc.