💡

Flutterでビジュアルリグレッションテストやってみた

2021/12/05に公開

※本記事はcommewアドベントカレンダー5日目の記事です。

Flutter開発におけるビジュアルリグレッションテストをしてみました。
ビジュアルリグレッションテストはよくReactなどのフロント開発で用いられるテスト手法ですがFlutterでも出来たら良いなと思い試してみました。

まずはかる~くビジュアルリグレッションテストの説明です。

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

コンポーネントやページのスクリーンショットを、事前に用意した正解の画像と比較することで、ピクセルレベルでの差分を検出するテスト手法のことです。
引用元:https://tech.smartcamp.co.jp/entry/Introducing-visual-regression-testing

ビジュアルリグレッションテストの実際の動作イメージ
image
実際のreg-suitを使った時の表示です。
ちょっとわかりにくいのですが右が変更前、左が変更後の画像です。
※画面は一気に複数パターンの端末で表示を行った時のテスト画像になっているため一つの画像に4種類表示されてます。
右下のプラスボタンを非表示にする対応を行ったので右と左でその差分が赤く囲まれています。

もうちょっと具体的なVRTの詳しい説明は以下の記事など詳しい方に譲ります。
https://zenn.dev/kyoncy/articles/7b3fad3f1d88c4
本記事ではFlutterでの導入の仕方を書きまっす!それではやっていきます。

アジェンダ的なやつ

    1. サンプルアプリ立ち上げ
    1. Golden_toolkit導入
    1. Golden_toolkitでのテストの実装
    1. GCSで公開用のバケット作成
    1. reg-suitでVRTを実装
    1. CircleCIにテストを導入
    1. DockerImageの作成、Dockerhubにイメージをプッシュ
    1. VRT動作確認

用意するもの

  • golden_toolkit(Flutterライブラリ)
  • GCS(AWS S3でも可)
  • reg-suit
  • CircleCI(今回CIサービスはこれを使用。ほかでもできる)

1. サンプルアプリ立ち上げ

FlutterのVRTを試す用のサンプルアプリを以下コマンドで作る

flutter create app

2. Golden_toolkit導入

以下コマンドで追加ァ!

flutter pub add golden_toolkit

上記コマンドにより、pubspec.yamlに次のような行が追加される。

dependencies:
  golden_toolkit: ^0.12.0

https://pub.dev/packages/golden_toolkit

3. Golden_toolkitでのテストの実装

以下ファイルを作ってテスト実行前に読み込まれる設定ファイルを追加
app/test/flutter_test_config.dart

import 'dart:async';
import 'package:golden_toolkit/golden_toolkit.dart';

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  // フォント読み込む
  await loadAppFonts();
  return testMain();
}

dart_test.yamlファイルをつくっておかないと、以下のように言われるのでとりあえず作っておく

Warning: A tag was used that wasn't specified in dart_test.yaml.
  golden was used in the test "DeviceBuilder - one scenario - override devices"

dart_test.yaml

tags:
  golden:

テスト用画像を保存する用の以下ディレクトリをつくる

mkdir app/test/goldens

app/test/golden_test.dart

import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:app/main.dart';

void main() {
  testGoldens('DeviceBuilder - one scenario - override devices',
      (tester) async {
    final builder = DeviceBuilder()
      ..overrideDevicesForAllScenarios(devices: [
        Device.phone,
        Device.iphone11,
        Device.tabletPortrait,
        Device.tabletLandscape,
      ])
      ..addScenario(
        widget: const MyApp(),
        name: 'default page',
      );

    await tester.pumpDeviceBuilder(builder);

    await screenMatchesGolden(
        tester, 'flutter_demo_page_single_scenario_more_devices');
  });
}

devicesは利用想定をしている端末を選ぶ。

以下コマンドを実行することでGoldentestが走る

flutter test --update-goldens

以下ディレクトリに画像出力されていることを確認
app/test/goldens
flutter_demo_page_single_scenario_more_devices.png
image

4. GCSで公開用のバケット作成

※本記事はGCSのバケット作成の記事ではないので設定などを軽く説明する程度にしてます。

バケットを作る
以下にアクセスし、「バケットを作成」から適当にバケットを作る
https://console.cloud.google.com/storage

作成後、「公開アクセス」をインターネットに公開
権限を追加から以下のような設定で追加する

新しいプリンシパル:allUsers
ロール:Storage オブジェクト閲覧者

参考
https://qiita.com/mako0715/items/a2049d31915f10f40681

5. reg-suitでVRTを実装

以下コマンドでreg-suitをインストール

npm init -y
npm install reg-suit

初期設定を行う

npx reg-suit init

基本的にはエンターでそれ以外は以下の設定で行いました。

? Plugin(s) to install (bold: recommended)
# 下記のプラグインをインストールする
- reg-keygen-git-hash-plugin : Detect the snapshot key to be compare with using Git hash.
- reg-notify-github-plugin : Notify reg-suit result to GitHub repository
- reg-publish-gcs-plugin : Fetch and publish snapshot images to Google Cloud Storage.

? Directory contains actual images.
# goldentestの画像を保存する箇所を指定ここでは test/goldens を指定

? notify-github plugin requires a client ID of reg-suit GitHub app. Open installation window in your browser Yes
# ブラウザが立ち上がるので対象のリポジトリを選択して、ClientIDを貼り付ける
? This repositoriy's client ID of reg-suit GitHub app <ClientID>

? Create a new GCS bucket No
# 4. GCSで公開用のバケット作成で作成したGCSのバケット名を入力
? Existing bucket name <GCSのバケット名>

.gitignoreに追加しとく

node_modules
test/goldens

6. CircleCIにテストを導入

.circleci/config.ymlファイルを作成
今回はリポジトリにvrt_appディレクトリを作成し、その中のテストを行うよう記述した

.circleci/config.yml

version: 2.1
jobs:
  vrt-app-visual-test:
    docker:
      - image: ohyeahseakit2/flutter_android:latest
        environment:
          GOOGLE_APPLICATION_CREDENTIALS: "/home/circleci/repo/gcloud-service-key.json"
    working_directory: ~/repo
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: true
      - run:
          name: setup GCP
          command: echo $GCLOUD_SERVICE_KEY | base64 -d > /home/circleci/repo/gcloud-service-key.json
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "vrt_app/package.json" }}-{{ checksum "vrt_app/package-lock.json" }}
            - v1-dependencies-
      - run:
          name: npm install
          command: npm install
          working_directory: ~/repo/vrt_app
      - save_cache:
          paths:
            - node_modules
          key: v1-dependencies-{{ checksum "vrt_app/package.json" }}-{{ checksum "vrt_app/package-lock.json" }}
      - restore_cache:
          keys:
            - dart-dependencies-{{ checksum "vrt_app/pubspec.lock" }}
            - dart-dependencies-
      - run:
          name: Install Dependencies
          command: flutter pub get
          working_directory: ~/repo/vrt_app
      - save_cache:
          paths:
            - ~/repo/vrt_app/.dart_tool
          key: dart-dependencies-{{ checksum "vrt_app/pubspec.lock" }}
      - run:
          name: Run tests golden file update
          command: flutter test --update-goldens
          working_directory: ~/repo/vrt_app
      - run:
          command: npm run regression
          working_directory: ~/repo/vrt_app

workflows:
  vrt-app-visual-test:
    jobs:
      - vrt-app-visual-test

CircleCIの環境変数にGCSのキー設定が書かれたJSONファイルをbase64でエンコードしたものを追加
GOOGLE_APPLICATION_CREDENTIALS
詳しいやり方は以下の記事を参考に

参考記事
https://roppongi-vue2.firebaseapp.com/#33

7. DockerImageの作成、Dockerhubにイメージをプッシュ

Flutterの最新バージョンが使えるDockerImageで丁度いいのがなかったため自作
(たぶんCodeMagicとか使ったほうが楽かもしれない。でも意地でもCircleCIを使いたかったんや…)

以下、実際のDockerFileの内容

# BaseはCircle CIのものにする。android sdk, gcloudが既に入っているので楽
FROM cimg/android:2021.10.2-browsers

# Install firebase tools
RUN sudo curl -sL https://firebase.tools | bash

# Install tools for easylauncher
RUN sudo apt-get update && sudo apt-get install -y fontconfig ttf-dejavu

# Chrome install
RUN sudo curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN sudo apt-get install -y ./google-chrome-stable_current_amd64.deb
RUN rm google-chrome-stable_current_amd64.deb

# Install Flutter SDK
# ほぼ以下のファイルのコピペ。flutter_verisonの初期化とprecacheの部分は独自。
# https://github.com/cirruslabs/docker-images-flutter/blob/master/sdk/Dockerfile
ARG flutter_version=stable

ENV FLUTTER_HOME=${HOME}/sdks/flutter \
    FLUTTER_VERSION=$flutter_version
ENV FLUTTER_ROOT=$FLUTTER_HOME
ENV ANDROID_TOOLS_ROOT="/opt/android_sdk"

ENV PATH ${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin

RUN sudo git clone --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME}
RUN sudo chown -R $(whoami) ${FLUTTER_HOME}

RUN ${FLUTTER_HOME}/bin/flutter precache

WORKDIR ${HOME}
RUN pwd
RUN flutter doctor -v

あとでGithubとか公開したい。

8. VRT動作確認

mainブランチに上記の手順で追加した内容をPush
次にdevelopブランチなどを作り画面の変更を行いPRを作成する
すると以下の画像のように変更差分があったコメントと画像による変更差分が確認できるURL出てくる

PR内でコミットをすると以下のようなコメントがreg-suitによってされる。
image

リンク先をクリックすると設定した画像の変更差分が簡単に見えるようになっている!
image

今回作成したPRの実際のリンク
https://storage.googleapis.com/flutter_playground-vrt_app/e392b18b16756a7a8212ce1de449721018e328ce/index.html
変更差分はいろいろな方法で確認できるのでスクリーンショットを取った箇所の変更差分が一目瞭然!

まとめ

Flutterでもビジュアルリグレッションテストができた!
Golden_toolkitによるスクリーンショットのとり方をもうちょっと学ばないといけなさそう
その他、スクリーンショットがとれるライブラリなどあればそれで代用も可能っぽい

FlutterでVRTする際にちょっとつまったところ

  • Golden_toolkitでVRTの画像を作成するのにフォントが豆腐になる
  • GCPでアップロード先の設定(権限やアクセスKeyのJSON作る方法とか)
    • GCSのシークレットキーとかって一度base64でエンコードしてからCircleCIの環境変数に入れるのかぁなるほど〜となった。
    • base64でエンコード!その手があったかと地味に勉強になりました。
    • 参考サイト(https://roppongi-vue2.firebaseapp.com/#33)
GitHubで編集を提案

Discussion