この記事は、Money Forward Engineering 1 Advent Calendar 2023 8日目の投稿です。
7日目は Yuito Nakamori さんで「コネクテッドシートによるBigQueryのコスト爆発を未然に防ぐ」でした!
はじめに
こんにちは。 2023年春に新卒で入社し、現在マネーフォワードホームカンパニーで(以下、ホームカンパニー)モバイルエンジニアを務めていますとしきです。主にマネーフォワードMEの開発に携わっています。 大学4年次の4月から1年間、インターンとして同社に在籍し、主にサーバーサイドの開発に携わりました。 入社2ヶ月ほど前にFlutterに挑戦する機会をいただいて、Flutterの魅力にどっぷりはまりモバイルエンジニアへの転向を経て新卒で入社しました。 そんな自分が入社して最初に行ったタスクである、Flutterの既存プロダクトにGolden Testを導入した話をしたいと思います。
Golden Testとは
Golden Testは、主にUIのリグレッションテストに用いられる手法で、アプリケーションのウィジェットやスクリーンショットの外観を検証するために使用されます。 このテストは、ウィジェットや画面のレンダリング結果を画像ファイル(Goldenファイル)として保存し、将来的な変更が発生した際に、新たに生成されたテスト結果とこれらの保存済みの画像とを比較することで一貫性を確認します。 Goldenファイルは、アプリケーションのビジュアルアスペクトが望ましい状態にあるときに生成され、このファイルがテストの「基準」となります。後にコードの変更が行われたときに、変更によってUIに意図しない影響が出ていないかを確認するために、新しいテスト結果をGoldenファイルと比較します。 この比較により、ピクセル単位での変更が検出され、意図しないレイアウトの変更やビジュアルの変化を捕捉することができます。
間違い探し
ここで間違い探しをしてみたいと思います。 下の画像において左が古いGoldenファイル、右が新しいGoldenファイルだとします。新しいGoldenファイルでは3つの変更が加わっています。皆さんは3つの差分を見つけられますか?
正解は、
- 2が全角数字になった
- ドメイン名が変わった
- テキストフィールドとボタンとの間が狭くなった
でした! 今回は例題のために用意した変更なので、自然と同じような状況になることは少ないと思います。 しかし、今回のように目視だけでは見落としてしまう変更があった時に効果を発揮するのがGolden Testです。
テスト実行時に差分が存在する場合テストが失敗します。 テストが失敗した場合は下の画像のような差分が可視化されたファイルが生成され、変更が加わった箇所を理解することが容易になります。
Golden Testを書いてみる
pubspec.yamlにgolden_toolkitを追加して、flutter pub get
を実行します。
ここからは簡単です。次のように記述してください。
void main() { TestWidgetsFlutterBinding.ensureInitialized(); // テストで使用するデバイスを定義(ここではPixel 4A) Device pixel = const Device( name: "Pixel4A-Android", size: Size(1080, 2340), safeArea: EdgeInsets.only( top: 44, bottom: 34 ), ); setUpAll(() async { await loadAppFonts(); // アプリのフォントをロード }); // テストシナリオの設定 testGoldens("Golden Test", (tester) async { final builder = DeviceBuilder() ..overrideDevicesForAllScenarios(devices: pixel) ..addScenario( widget: MaterialApp( home: Sample(), // テスト対象のWidget ), ); await tester.pumpDeviceBuilder(builder); // デバイスビルダーをテスターにセット await screenMatchesGolden(tester, "sample"); // 画面がGoldenファイルと一致するかチェック }, tags: "golden"); }
これだけでGolden Testを実行する環境が整ってしまいました。
まずはGoldenファイルを作成したいのでflutter test --update-goldens
を実行します。
これで最新のGoldenファイルが生成されたので、それぞれのテストフローにflutter test
を含めることで完成です。
ホームカンパニーにおけるFlutter
マネーフォワードMEでは現在、新規作成する画面は基本的にFlutterで実装しています。同じプロダクト開発部でモバイルエンジニアとして働く椎名さんが以前執筆してくれたマネーフォワード MEのモバイル開発の生産性を爆上げした事ランキング に詳しくまとめられていますので、ぜひ参照してみてください!
なぜGolden Testを導入したのか
Golden Testを導入した背景として、何か大きな課題があったからではなく、自分が興味を持ったことがきっかけです。 詳しい状況を全く覚えていませんが、先ほども登場した椎名さんとの雑談中にGolden Testの存在を教えてもらいました。 調べてみたとこと「もっと触ってみたい!」と思いチームに相談したところ、2つ返事でOKをもらえました。 OJTが始まったばかりにもかかわらず、自分主体で進めさせてもらえたことは、本当にありがたい環境だなと改めて思います。 話は戻りますが、仮に自分が今の状態で半年ほど前に戻ったとしたら今よりも積極的にGolden Testを導入を進めると思います。 その理由は、「デザイナー、エンジニア間でUIコンポーネントに対する認識合わせ」を保証することが可能になったからです。
デザイナー、エンジニア間でUIコンポーネントに対する認識合わせ
私たちのリポジトリには様々なUIコンポーネントが存在します。 これらのコンポーネントに対して「デザイナー、エンジニア間でUIコンポーネントに対して共通認識を持っていること」が重要なルールとなっています。 ここでいう共通認識とは、デザイナーとエンジニア間でそのUIコンポーネントのサイズ、色、その他のプロパティに関して同じ理解を共有している状態を指します。 例えば、新しくボタンをコンポーネント化したい場合、コンポーネント化したいボタンのデザインをデザイナーが作成しエンジニアが実装を行うことになりますが、取り掛かる際には両者がそのUIコンポーネントに関するサイズや色などのプロパティについて同じ理解をしている必要があります。 つまりエンジニアの考えだけでUIコンポーネントの作成・更新はやるべきではないですし、たとえ間違って作成・更新されたとしても使用されるべきではありません。 しかし、エンジニアが適切でないと理解していても、意図せずにそのような状況が発生することがあります。 例えば、Flutterのバージョンのアップデートが原因になることも考えられます。Flutterのバージョンアップを経て、UIコンポーネントに変更が加わってしまったとします。 この場合、意図していないUIコンポーネントに対する変更なので、後になって「あれ、なんかデザインと実装違くないか?」という状況が起きてしまいます。 この状況を生まないために、全UIコンポーネントに対して都度目視で変更が加えられていないかを確認することは、容易ではありません。 そんな時にGolden Testがあればこのような悩みを解消することができます。
導入の効果
私たちは、冒頭のクイズで述べたような複数のコンポーネントを並べたWidgetを作成し、そのWidgetに対してGolden Testを実行して、意図しないUIコンポーネントの変更を防いでいます。 しかし実際にはGolden Testを導入してから、意図していないUIコンポーネントに対する変更は起きておらず導入した恩恵を大きくは受けられていません。 一方でその要因として考えられることがあります。 それはエンジニア間で先ほど述べた「デザイナー、エンジニア間でUIコンポーネントに対して共通認識を持っていること」のルールが浸透していることにあると思います。 自分自身Golden Test導入後、確実にコンポーネントに対する意識の仕方は変わりました。自分のように新しくチームに参加した場合にもルールを普及するいい手段になると思っています。 そのように考えると、導入して得られたことは大きかったと思います。
おわりに
今回のGolden TestはWidget Testに分類されますが、そのほかのUnit TestやIntegration Testなどと組み合わせることで、より強力なテストを記述することが可能になります。 Flutterのテストに関する詳細はこちらを参照してみてください。 いかがでしたでしょうか。ここまで読んでくれたあなたはGolden Testを試してみたくなったでしょうか。 改めて、自分の興味ベースで何事にも挑戦させてくれるチームメンバーには感謝しかありませんし、少しでも自分から還元できるものを増やしていきたいと思います。