📝

Flutterのアプリにゴールデンテストを導入

に公開

はじめに

アプリを作ろうと思ったんですが、いろいろ始める前に、なにかするとすぐ画面が崩れるので、知らないところでそうならないよう、画面の差分があったときにはちゃんと来づけるようにしたい…と思い、ゴールデンテストを導入することにしました。

…というわけで設定作業を始めたのですが…

ちゃんとアプリの画面が取れるようになるまでに苦労があったので、そこをシェアしようかな、と思った次第です。

結論

alchemistのサンプルには、ListTileのテストが書かれていまして…そのままアプリのページのMyApp(flutter create直後にできるヤツ)を置き換えても、ものすごい大量の例外で圧倒されてしまいました。

大量の警告をつぶさに読んでいくと、ウィジェットのサイズが指定されていないことが問題だったことがわかり、SizedBoxで包んであげればよいことがわかりました。

// 省略
goldenTest(
  'Run my_app',
  fileName: 'my_app',
  builder:
    () => GoldenTestGroup(
      children: [
        GoldenTestScenario(
          name: 'my_app',
          child: const SizedBox(width: 400, height: 800, child: MyApp()), // ココ!
        ),
      ],
    ),
);
// 省略

詳細

ここからは自分で設定したプルリクエストのまとめ的なものです。

できあがりの姿

  • CI(Github Actions)でテスト、保存されているゴールデンファイルと差分が見つかったときにはエラー。
    • 差分をダウンロードして確認
  • 差分のできることがわかっているときにはゴールデンファイルをプルリクエストに追加
  • 導入時のプルリクエストはこちら
    • podcast playerって書いてあるけど、まだ全然進んでないのは御愛嬌w

ツール

ゴールデンテストを簡易に行えるようにするツールがいくつかあるようですが、その中からよく使われていそうなalchemistを選択しました。

https://pub.dev/packages/alchemist

alchemistでなるほどと思ったこと

READMEを見ていて、画像による比較で差分が出やすいのはフォントなので、全部■で表示することを進めていました。また、UIの影も、バージョンなどで変わりやすいのでナシが選べたりして、興味深かったです。

pubspec.yaml

alchemistを追加しただけです。

pubspec.yaml
# 省略
dev_dependencies:
  alchemist: ^0.11.0
  flutter_lints: ^5.0.0
  flutter_test:
    sdk: flutter
# 省略

test/flutter_test_config.dart

CIで見るので、テスト時に実行したマシン向けの結果を作らないようにしています。

test/flutter_test_config.dart
import 'dart:async';
import 'package:alchemist/alchemist.dart';

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  return AlchemistConfig.runWithConfig(
    config: const AlchemistConfig(
      platformGoldensConfig: PlatformGoldensConfig(enabled: false),
    ),
    run: testMain,
  );
}

テスト

(冒頭の結論のところと同じです。)

alchemistのサンプルには、ListTileのテストが書かれていまして…そのままアプリのページのMyApp(flutter create直後にできるヤツ)を置き換えても、ものすごい大量の例外で圧倒されてしまいました。

大量の警告をつぶさに読んでいくと、ウィジェットのサイズが指定されていないことが問題だったことがわかり、SizedBoxで包んであげればよいことがわかりました。

// 省略
goldenTest(
  'Run my_app',
  fileName: 'my_app',
  builder:
    () => GoldenTestGroup(
      children: [
        GoldenTestScenario(
          name: 'my_app',
          child: const SizedBox(width: 400, height: 800, child: MyApp()), // ココ!
        ),
      ],
    ),
);
// 省略

ゴールデンファイル

デザインを変えたり、新しいテストを追加したりしたときにはゴールデンファイルをflutter test --update-goldensで更新する必要があります。

Github Actions

テストの失敗時に差分などの情報が格納されているディレクトリをアーティファクトとしてアップロードしています。

.github/workflows/flutter_test.yml
# 省略
      - name: Run tests
        run: flutter test
      # 失敗時だけtest/golden/failuresディレクトリをアーティファクトにアップロードしてます。
      - name: Upload golden failure images
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: golden-failures
          path: test/golden/failures
# 省略

アップロードされたアーティファクトはジョブ内のステップを開くとダウンロードパスが書かれています。

ちなみにこんな感じのものです。(フォントを■にする前に動かしたら…まんまと差分が検知されました💦)

おわりに

ひさしぶりにflutterを触りました。いろいろ忘れててもう…。もっと少しずつでもいいから触っていこ…。
あとは…「はじめに」にも書きましたが、おんなじところでひっかかったひとに届いてくれればいいな、という感じです。

参照

Discussion