【最新版】FlutterにPatrolのテスト導入・環境構築

【最新版】FlutterにPatrolのテスト導入・環境構築

Patrolとは

Patrolは、Flutterアプリ専用に設計され、2022年 9 月にリリースされた強力なオープンソースのUIテストフレームワークです。

特に、Flutterテスト内でネイティブプラットフォーム機能を活用できることや、直感的なテスト作成ができることから選ばれる理由が多いようです。

  • 権限ダイアログ通知WebViewを操作
  • デバイス設定Wi-Fi のの変更、切り替えなど

それでは早速導入していきましょう。

patrol_cliをインストール

% flutter pub global activate patrol_cli

インストールが成功し、環境が適切に設定されていることを確認します。

% patrol doctor

プラットフォームのすべてのチェックが緑色になっている必要があり、以下のように赤色の部分は解消する必要があります。

エラー① Program ideviceinstaller not found

以下コマンドで解決しましょう。

% brew install ideviceinstaller

エラー② Env var $ANDROID_HOME is not set.エラー

• Env var $ANDROID_HOME is not set. (See the link: <https://developer.android.com/tools/variables#set>)

.zshrcファイルに以下を追加して、source ~/.zshrcで反映してください。

export ANDROID_HOME=$HOME/Library/Android/sdk
export ANDROID_SDK_ROOT=$ANDROID_HOME
export PATH="$PATH:$ANDROID_SDK_ROOT/platform-tools"

全てのエラーが解消できて緑になったらOKです。

patrolパッケージの依存関係を追加

パッケージへの依存関係を追加します。

注意点:パッケージには、Android SDK バージョン21以上が必要です。

% fvm flutter pub add patrol --dev 

今回は、patrol: ^3.15.2を使用しています。

pubspec.yamlにpatrolセクションを作成

Androidではパッケージ名、iOSではバンドルIDを設定します。

dependencies:
  # ...

dev_dependencies:
  # ...

patrol:
  app_name: My App
  android:
    package_name: com.example.myapp
  ios:
    bundle_id: com.example.myapp

Androidの統合設定 (Groovy ビルド構成ファイルを使用するプロジェクト)

ここからはAndroidとiOSで別々の設定が必要になります。

まずは簡単なAndroidから設定していきます。

MainActivityTest.java の作成

プロジェクトディレクトリのandroid/app/src/androidTest/java/com/example/myapp/に移動しましょう。

該当するフォルダがない場合は作成してください。(myappなどはアプリのパッケージ名に置き換えてください。)

そして、**MainActivityTest.java**という名前のファイルを作成し、そこに以下のコードをコピーします。

package com.example.myapp; // replace "com.example.myapp" with your app's package

import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import pl.leancode.patrol.PatrolJUnitRunner;

@RunWith(Parameterized.class)
public class MainActivityTest {
    @Parameters(name = "{0}")
    public static Object[] testCases() {
        PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
        instrumentation.setUp(MainActivity.class);
        instrumentation.waitForPatrolAppService();
        return instrumentation.listDartTests();
    }

    public MainActivityTest(String dartTestName) {
        this.dartTestName = dartTestName;
    }

    private final String dartTestName;

    @Test
    public void runDartTest() {
        PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
        instrumentation.runDartTest(dartTestName);
    }
}

build.gradleファイルの修正

プロジェクト ディレクトリのandroid/appフォルダーにあるbuild.gradle.ktsファイルに移動します。

なければ新しくファイルを作成しましょう。

以下のコードを書くセクションに対して追加してください。

  • testOptions:execution = "ANDROIDX_TEST_ORCHESTRATOR"
  • defaultConfig:testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner"testInstrumentationRunnerArguments clearPackageData: "true"
  • dependencies:androidTestUtil "androidx.test:orchestrator:1.5.1"
...

android {
    ...
    testOptions {
        execution = "ANDROIDX_TEST_ORCHESTRATOR"
    }
    defaultConfig {
		    ...
        testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner"
        testInstrumentationRunnerArguments clearPackageData: "true"
    }
}

dependencies {
		...
    androidTestUtil "androidx.test:orchestrator:1.5.1"
}

iOSの統合設定

ios/Runner.xcworkspace をXcodeで開きます。

Targetsの作成

Targetsの左下の+ボタンからRunnerUITestsという名前で作成します。

RunnerUITests targetの作成

Organization Identifierなどを入力してLanguegeに関しては、Objective-Cで作成するようにしましょう。

作成すると、以下2つのファイルが作成されます。

  • RunnerUITests.m
  • RunnerUITestsLaunchTests.m

ここで、RunnerUITestsLaunchTests.m は使わないので削除しておきます。

iOS デプロイメント ターゲットの確認

ビルド設定セクションの iOS デプロイメント ターゲットがプロジェクトと同じであることを確認してください。

サポートされている最小のiOS デプロイメント ターゲットは13.0です。

iOS デプロイメント ターゲットの確認

RunnerUITests.mの修正

先ほど生成されたRunnerUITests.mを以下のコードにします。

@import XCTest;
@import patrol;
@import ObjectiveC.runtime;

PATROL_INTEGRATION_TEST_IOS_RUNNER(RunnerUITests)

Podファイルの修正

新しく作成したターゲットを**ios/Podfile**に埋め込みましょう。

target 'Runner' do
  ...

  target 'RunnerUITests' do
    inherit! :complete
  end
end

ビルドフェーズの追加

RunnerUITests > Build Phasesに移動し、2つの新しい「Run Script Phase」ビルドフェーズを追加します。

それぞれにxcode_backend buildxcode_backend embed_and_thin と命名してください。

xcode_backend build

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build

xcode_backend embed_and_thin

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed_and_thin

また、以下の順番になるように配置を修正しましょう。

ビルドフェーズの追加

User Script SandboxingをNoにしておく

RunnerUITestsのUser Script SandboxingをNoに設定しましょう。

iOS デプロイメント ターゲットの確認

簡単な統合テストを作成する

Patrolが正しく設定されていることを確認するために、ダミーのFlutter統合テストを作成してみましょう。

プロジェクト配下(integration_test/example_test.dart)に次のコードを貼り付けます。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

void main() {
  patrolTest(
    'counter state is the same after going to home and switching apps',
    ($) async {
      // Replace later with your app's main widget
      await $.pumpWidgetAndSettle(
        MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('app')),
            backgroundColor: Colors.blue,
          ),
        ),
      );

      expect($('app'), findsOneWidget);
      if (!Platform.isMacOS) {
        await $.native.pressHome();
      }
    },
  );
}

以下コマンドを実行してテストします。

% patrol test -t integration_test/example_test.dart -d デバイスID

デバイスIDは% flutter devicesコマンドで使用したいシミュレーターなどのIDを入れてください。

テストが成功するとこのようになります。

Patrolテスト成功の結果

patrol testに失敗する解決方法5選

ここでは実際に、% patrol testを実行してこけて解消したエラーを紹介します。

pubspec.yamlにintegration_testを追加する

自分の実行環境では、integration_testを追加しないと実行できませんでした。

  patrol: ^3.15.2
  integration_test:
    sdk: flutter

podとXcodeのキャッシュ削除・再インストール

Xcodeを開いている場合は一旦閉じて以下コマンドをプロジェクトのルートで実行します。

% rm -rf ios/Pods ios/Podfile.lock pubspec.lock \\
  && rm -rf ~/Library/Developer/Xcode/DerivedData \\
  && cd ios && pod cache clean --all && pod install --repo-update \\
  && cd .. &&  fvm flutter clean && fvm flutter pub get

Parallelizationを無効にする

Product > schema > edit schemaを開き、RunnerUITestのOptionからParallelizationをDisabledにしましょう。

Parallelizationを無効にする

Xcodeのフォーマットを最新にする

ここが古いと、ArgumentError - [Xcodeproj] Unable to find compatibility version string for object version 70.エラーのようなものが出るので、最新にしましょう。

Build Phase Scriptの修正

以下のようにコードの修正を行いました。

xcode_backend build

#!/bin/sh
set -e

export FLUTTER_BUILD_DIR=${FLUTTER_BUILD_DIR:-build}

"/bin/sh" "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build

xcode_backend embed_and_thin

#!/bin/sh
set -e

export FLUTTER_BUILD_DIR=${FLUTTER_BUILD_DIR:-build}

"/bin/sh" "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed_and_thin

まとめ

意外とネイティブ統合のための設定が複雑かつ情報も少なかったたエラーに悩まされ、苦労しました。

まだちゃんとテストをかけていないので、次回Patrolの記事を出す際は、テストについて書こうと思います。

記事を書いた人

Matsuura

エンジニア

Matsuura

Anycloudでエンジニアしてます!主にFlutter・Typescript