
はじめに
こんにちは、ZOZOTOWN開発2部Androidブロックの大江です。普段はZOZOTOWN Androidの開発を担当しています。
UIのリグレッション防止を目的として、2024年からVisual Regression Testing(VRT)の導入に取り組んできました。その第1弾として共通UIコンポーネントのモジュールに対してJetpack ComposeのPreviewを使ったVRTを運用しています。
本記事では、VRT導入について、VRTをCI/CDツール上で実行するための工夫を中心に紹介します。
目次
ZOZOTOWN Androidの課題
ZOZOTOWN Androidは画面の数が多く、長年の開発によってモジュールの依存関係やUIコンポーネントの共通化の仕組みが複雑になっています。そのため、レイアウト変更を伴う開発では、変更した箇所以外の画面にも影響を及ぼす可能性があります。
こうした影響を漏れなく把握することは難しく、レビュー時の目検だけでは、意図しないレイアウトの変化を見落としてしまうケースが多くありました。これらの課題を解決するため、私たちはVisual Regression Testing(VRT)の導入を決定しました。
VRTとは
Visual Regression Testing(VRT、ビジュアルリグレッションテスト)は、レイアウトの差分を自動的に検出するテスト手法です。アプリケーションの実装を変更する前後でスクリーンショットの画像を生成して、両者を比較することで意図しないレイアウトの変更を検出します。VRTを導入することで、目検では見落としやすいレイアウトの変更を自動で検出できるようになります。
VRTは強力な仕組みですが、画像を大量に生成するため、マシンのリソースを消費しやすいなどの課題があります。そのためGitHub Actionsなどで実行する際には冗長な処理を防ぐことが重要です。
VRTの冗長な処理を防ぐための工夫
VRTの冗長な処理を防ぐための工夫を説明します。実際に運用しているGitHub ActionsでPull Requestに対してVRTを適用するフローを紹介します。

例として以下の状況のPull Requestに対してVRTを適用した場合を考えてみます。
- develop branchの最新commitのhashがf9c2dcc
- develop branchからfeature branchを作成
- feature branchに対してhashがce56c9dとbfe0a66のcommitを追加
- feature branchをdevelop branchにmergeするPull Requestを作成

わかりやすくするために、VRTを以下の3つのステップに分けて説明します。
- 画像生成
- 画像比較
- 結果出力
画像生成
まず実装を変更する前後のアプリケーションに対して画像を生成します。ZOZOTOWN Androidでは、Roborazziというライブラリを使用してComposableのPreviewの画像を生成しています。
実装を変更する前後としてPull Requestのbase branchとhead branchの画像をそれぞれ生成します。画像の生成はマシンのリソースを消費しやすいため、冗長な処理を防ぐことが重要です。そこで生成した画像を可能な限り使い回すように工夫しました。
head branchはcommit&pushされて実装が変更される度に画像を生成する必要があります。一方、base branchはPull Requestがmergeされるまで変更されず、複数のPull Requestで同じbase branchが参照されることもあります。そこで生成したbase branchの画像をbase branchの最新のcommitのhashに紐づけて保存しておき、このhashが変わらない限り使い回すようにしています。
このように生成した画像をcommitのhashに紐づけて保存するためにアプリケーションのプロジェクトとは別にGitHubのRepositoryを用意しました。画像保存用のRepositoryに対して、commitのhashをbranch名として画像を保存することでこの仕組みを簡単に実現できました。前述の例ではf9c2dccというbranch名でbase branchのアプリケーションの画像が保存されます。
この仕組みを次の図に表しました。

画像比較
次にPull Requestのbase branchとhead branchの画像を比較します。
Pull Requestにcommitがpushされる度にhead branchの画像を生成し、base branchは最新のcommitのhashに紐づいた画像がない場合のみ画像を生成します。両者の画像が揃ったところで画像を比較します。
ZOZOTOWN Androidでは、画像の比較にも前述のRoborazziというライブラリを使用しています。Roborazziはコマンドを実行するだけで画像を比較し、その結果を出力してくれます。
比較結果は差分を抽出した画像として出力されます。例として、ボタンの横幅がわずかに変わってしまっていることを検出した際の出力結果を載せておきます。

このように目検では見落としがちな意図しないレイアウトの変更をVRTによって検出できます。
結果出力
最後に比較結果の画像を出力してPull Requestに添付します。
Pull Requestにはリンクで比較結果の画像を添付するため、比較結果の画像をクラウドストレージに保存する必要があります。ZOZOTOWN Androidでは、前述したアプリケーションの画像生成の工程と同様に、比較結果の画像もGitHubのRepositoryに保存しています。ただし管理を容易にするため、アプリケーションの画像を保存するRepositoryと比較結果の画像を保存するRepositoryは分けています。
比較結果の画像はPull Requestと紐づけて管理します。Pull Requestのhead branchとbase branchのbranch名を_to_でつないだ値をbranch名として保存しています。前述の例ではfeature_to_developというbranch名で比較結果の画像が保存されます。
まとめ
本記事では、ZOZOTOWN AndroidにおけるVisual Regression Testing(VRT)の導入について紹介しました。
現状では共通パーツのみにVRTを適用しているため、全体の約14%のComposableがVRTの対象となっています。今後、VRTを適用するモジュールを増やすなど、VRTをさらに活用していきます。Android開発でVRTの導入を検討されている方がいれば、ぜひ本記事を参考にしてみてください。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。