fukke.cafe

ビジュアルリグレッションテストを導入する

ビジュアルリグレッションテストとは

ウェブサイトの見た目が崩れていないことを保証するテストです。
ビジュアルリグレッションテスト(以降VRTと呼ぶ)を導入することで新機能やリファクタリングによるデザインのデグレを防止でき、安定したリリースが可能になります。

VRTの仕組み

主流のVRTでは、始めにマスター環境のスクリーンショットを撮影し保存します。 そしてテスト時に撮影したスクリーンショットと保存してあったマスター環境のスクリーンショットと比較し その結果ピクセルのズレや色の違いがあれば通知、正しいスクリーンショットで上書きするというのが多いです。
しかし、この仕組みではスクリーンショットを保存する必要があるためCIと別にS3等のストレージ環境が必要になります。
今回はストレージを利用しない方法を導入します。 具体的にはテストごとにマスター環境とテスト環境でスクリーンショットを撮影し比較します。 毎回撮影するコストはかかりますがストレージに保存する手間を省けるためシンプルです。

Playwrightを使ったVRT

実装にはPlaywrightを用います。Playwrightはe2eテストフレームワークですがtoMatchSnapshotメソッドを使ってVRTができます。
index.spec.ts
const url = process.env.URL

test('', async (page) => {
  page.goto(url)

  expect(
    await page.screenshot()
  ).toMatchSnapshot()
})
playwright --update-snapshotsでスナップショットを生成。オプションなしで比較ができます。

テスト結果をレポートする

Playwrightではテスト結果をplaywright-reportとして生成する機能があります。 これをartifactsとしてダウンロードできるようにする、もしくは静的サイトとしてデプロイすると簡単にテスト結果が確認できるようになります。
他にもテスト結果をjunit形式で生成もできるためこれに対応している環境であれば簡単にレポートできます。

安定して回すために

VRTを導入しても動作が安定しなければ開発者から嫌がられるかもしれません。 基本的にe2eは不安定なため少しでも安定して回せるような工夫が必要です。
例えばページ内にランダムな値が表示される可能性がある場合は確率で差分が出てしまいます。 そのような箇所は
await page.screenshot({
  masks: [page.locator('div.random-area')]
})
のようにマスクを被せることで回避できます。
またCSSアニメーションがあるページにはオプションanimations: disabledを利用してアニメーションを無効化できます。
APIからリアルタイムなデータをGETする場合は通信をモックしてあげましょう。
page.route('**/api/timeline', () => ({
  timeline: [{}, {}, {}]
}))
マスクできないような要素がでてきた場合はテスト環境内でJavaScriptを実行する手段もあります。
await page.evaluate(() => {
  document.querySelector('').style.diplay = 'none'
}).catch(() => {})

おわりに

ここまでできればあとは各々の環境のCIで実行するだけです。 CIの実行にはHeadlessChromeが必要です。先駆者が作ったImageがたくさんあるので探してみてください。
VRTで一番嬉しいのはテスト項目から見た目が変わってないことが消えることです。 リファクタリングも捗るのでサービスの品質向上にもつながります。 複雑なサービスでも通信のモック・マスク・JavaScriptの実行でなんとかなるので是非VRTを導入してみてください。

公開日 2022/12/29

目次

Thanks you for reading.