ビジュアルリグレッションテストを導入してみた
こんにちは。フロントエンドエンジニア兼社内広報のとよしーです。
先日、開発しているプロダクトで使っている外部のUIライブラリのバージョンアップの検証をしていたときに、予期せぬUIのレイアウト崩れが発生しました。
いまやプロダクトのUIコンポーネント数もページ数も多くなってきたので、全部の画面でUIがレイアウト崩れをしていないかを目視で確認するのはさすがにしんどいな〜と思ったわけでございます。眼精疲労まっしぐらです。
そこで、UIの差分を検知してくれるビジュアルリグレッションテストの導入をしよう!とチーム内でなりまして、今回はそのビジュアルリグレッションテストの導入やハマった点を共有していければと思います。
● ビジュアルリグレッションテストとは...?
ビジュアルリグレッションテスト(以下:VRT)とは、UIコンポーネントやページの見た目をテストするための仕組みです。
アプリケーションを実行したときの画像と前のバージョンの画像を、ピクセル単位で比較するため、UIのレイアウト崩れがすぐに検知できるようになることがメリットです。
冒頭でも申し上げた通り、VRTを導入すると予期せぬUIの変更やレイアウト崩れを目視で確認せずに検知できるので、工数削減やUI面での品質向上が見込める効果があります。
・ツールについて
私たちが最初に導入を考えたVRTのツールとしてreg-suitというものがあります。
こちらは、VRTのためのテスティングツールとなっています。
差分のレポートを作成する機能や、GitHubへのPullRequest通知やSlackへのテスト結果通知機能があり、充実した内容のテスティングツールです。
ただ、撮影したスナップショットを管理する場所として、クラウドストレージ(AWSのS3、GoogleCloudStorage等)の利用が必要です。
今回私たちのプロジェクトでは、クラウドストレージでの運用が難しいため、泣く泣く別のものを選定することになりました...。
最終的に私たちが今回選定したのはStorybookのアドオンであるStoryShots + Puppeteerです。
私たちはUIコンポーネントのドキュメントはStorybookで管理しているため、その点においても相性がいいのではないかと考えました。
StoryShots + Puppeteerを使うことによって、Storybookがビルドした結果を画像として保存しておき、ビジュアルリグレッションテスト時に変更があったかどうかを自動で判定することができます。
● 導入手順
こちらのQiitaの記事を参考に導入をしていきました。
● ハマったポイント
・macOSとWindowsでテスト結果が違った
私は普段macで仕事をしているので、mac環境でVRTを実行して動作検証をしておりました。全てのテスト結果がPASSしたところで、別のメンバーに協力していただきWindowsの環境でもVRTを実行していただきました。
そしたらなんとテストが通らないじゃありませんか...!
原因はOSの標準フォントの違いでした。
これはまいったな...と頭を抱えておりましたが、
「大元の比較する画像をWindowsとmacで別々にすればいいのでは?」
というアイデアが浮かんできました。
さっそくVRTを実行しているコードに、WindowsとmacのOS判定を加えました。
これでWindowsの人はWindowsでビルドされた画像を比較することができて、macの人はmacでビルドされた画像を比較することができるはず!
いい感じです!
(ただWindowsとmac以外の人のことは考慮できていないので、そこは課題ですね...)
・テスト失敗とするレベルの閾値を設定できる点
その後、VRTを他のチームメンバーのPCでも検証していただきました。
今度は1ミクロンくらい細かいUIの差分を検知してしまって、テストがコケてしまいました。
差分の値は、0.00005%くらいの差分とエラーログで吐き出されていました。
ここらへんはそれぞれのプロダクトによって決めるべき点だとは思いますが、私たちはここまで厳しく差分をチェックしなくてもいいのでは...?と感じました。
そこでStoryShotsのオプションで、テスト失敗とするレベルの閾値を設定できることがわかりました。
failureThreshold: テスト失敗とするパーセンテージを設定
failureThresholdType: テスト失敗とする型を設定
ここで、閾値のパーセンテージを設定することによって、テスト失敗の厳密さを設定できそうですね!
VRTを実行している最終的なコードはこちら
import initStoryshots from '@storybook/addon-storyshots'
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer'
import path from 'path'
const ua = window.navigator.userAgent.toLowerCase()
const isWindows = ua.includes('win32')
const isMac = ua.includes('darwin')
const os = isMac ? 'macOS' : isWindows ? 'Windows' : 'Other OS'
console.log(`running on ${os}...`)
// Windowsとmacで画像のディレクトリを分ける
const dir = isMac ? '__image_snapshots_mac__' : isWindows ? '__image_snapshots_win__' : null
if (dir) {
const getMatchOptions = ({ context: { kind, story }, url }) => {
return {
customSnapshotsDir: path.resolve(__dirname, '..', '..', '..', 'src', 'stories', '__tests__', dir),
failureThreshold: 0.2,
failureThresholdType: 'percent'
}
}
initStoryshots({
suite: 'Image storyshots',
test: imageSnapshot({
storybookUrl: 'http://localhost:6006/',
getMatchOptions
}),
configPath: path.resolve(__dirname, '..', '..', '..', 'config', 'storybook')
})
} else {
console.error('macOSかWindowsで実行してください')
}
● 今後の課題
・CIに組み込む場合、CI終了までにいままでより時間がかかってしまうこと
今回導入したVRTは、プロジェクト内でいつも使用しているCI/CDパイプラインには一旦盛り込まないことにしました。
いずれはCIのなかに今回のVRTも組み込むことによって、UIの保守性が高められることが期待されるのですが、VRTは実行してからテスト結果が出るまでがまぁまぁ時間がかかります。
つい最近CIの改善を行って少しだけCIの時間が短縮できた実績があるので、VRTをCIに導入することでまたCIの時間を延ばしてしまうのは、ちょっと気がかりなところです。
ここらへんは開発メンバーと話し合いつつ、今後解決していかねばなりませんね。
● まとめ
今回はVRTの概要から、私たちがハマった点・今後の課題を紹介しました。
ハマりどころはありましたが、VRTを導入したことにより外部のUIライブラリのアップデートによるUIの差分も検知できるようになったので、確実にメリットはあります。
プロダクトの規模が大きくなり、UIコンポーネントの数も多くなってきて管理が大変であれば、VRTの導入を検討してみてはいかがでしょうか。
少しでも参考になれば幸いです!
この記事が気に入ったらサポートをしてみませんか?