mcpField
F
flutter-add-integration-test

flutter-add-integration-test

github.com/flutterテスト・品質3.1k38.6k 回インストール2 日前に更新
リポジトリ

flutter-add-integration-test について

Flutter 開発者向けのインテグレーションテストガイドです。Dart/Flutter MCP server を使った UI と操作の探索を含め、設定、作成、実行方法を説明します。

主な機能

  1. 01依存関係とアプリのエントリーポイント設定
  2. 02MCP による Widget ツリーの確認
  3. 03タップ・入力・スクロールの再現
  4. 04エンドツーエンドテストの作成
  5. 05テスト実行とパフォーマンス計測

活用シーン

  1. 01テスト作成前のアプリ操作フロー探索
  2. 02Widget をまたぐユーザーフローの検証
  3. 03Chrome または Android でのテスト実行
  4. 04未マウント Widget やアニメーションのタイムアウト調査

MCP 対応

Dart/Flutter MCP server でアプリを起動し、Widget ツリーを確認して、タップ・テキスト入力・スクロールによる操作を試す方法を案内します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/flutter/agent-plugins --skill flutter-add-integration-test

インストール後、Claude Code で/flutter-add-integration-testを入力して読み込まれたか確認します。

詳細

Flutter 統合テストの実装

目次

プロジェクトの設定と依存関係

統合テストと Flutter Driver 拡張機能をサポートするようにプロジェクトを設定します。

  1. 必要な開発依存関係を pubspec.yaml に追加します。
    flutter pub add 'dev:integration_test:{"sdk":"flutter"}'
    flutter pub add 'dev:flutter_test:{"sdk":"flutter"}'
    
  2. アプリケーションのエントリーポイント(通常は lib/main.dart または専用の lib/main_test.dart)で Flutter Driver 拡張機能を有効にします。
    • package:flutter_driver/driver_extension.dart をインポートします。
    • runApp() の前に enableFlutterDriverExtension(); を呼び出します。
  3. テスト中に確実に対象を特定できるよう、アプリケーションコード内の重要なウィジェットに Key パラメーター(例:ValueKey('login_button'))を追加します。

MCP による対話的な探索

静的テストを作成する前に、Dart/Flutter MCP サーバーのツールを使用してアプリケーションの状態を対話的に探索、操作します。

  • 起動:target: "lib/main_test.dart" を指定して launch_app を実行し、アプリケーションを起動して DTD URI を取得します。
  • 検査:get_widget_tree を実行し、利用可能な Key、Text ノード、ウィジェットの Type を確認します。
  • 操作:tap、enter_text、scroll を実行してユーザーフローをシミュレートします。
  • 待機:画面遷移時やアニメーションの開始時には、必ず waitFor を実行するか、get_health で状態を確認します。
  • マウントされていないウィジェットのトラブルシューティング:ウィジェットがツリー内で見つからない場合、SliverList または ListView で遅延読み込みされている可能性があります。操作する前に scroll または scrollIntoView を実行してウィジェットをマウントします。

テスト作成のガイドライン

flutter_test API のパラダイムに従って統合テストを構成します。

  • プロジェクトルートに専用の integration_test/ ディレクトリを作成します。
  • すべてのテストファイルに <name>_test.dart という命名規則を使用します。
  • main() の冒頭で IntegrationTestWidgetsFlutterBinding.ensureInitialized(); を呼び出してバインディングを初期化します。
  • await tester.pumpWidget(MyApp()); を使用してアプリケーションの UI を読み込みます。
  • tester.tap() などの操作の後に await tester.pumpAndSettle(); を使用してフレームを進め、アニメーションの完了を待ちます。
  • expect(find.byKey(ValueKey('foo')), findsOneWidget); または findsNothing を使用してウィジェットの表示を検証します。
  • await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder); を使用して画面外にある特定のウィジェットまでスクロールします。

レガシーな flutter_driver の条件付きロジック:

  • レガシーな flutter_driver テストを維持または移行する場合は、WidgetTester API の代わりに driver.waitFor()、driver.waitForAbsent()、driver.tap()、driver.scroll() を使用します。

実行とプロファイリング

flutter drive コマンドを使用してテストを実行します。integrationDriver() を呼び出すホストドライバースクリプトを test_driver/integration_test.dart に配置する必要があります。

実行対象の条件

  • **Chrome でテストする場合:**別のターミナルで chromedriver --port=4444 を起動してから、次を実行します。 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome
  • ヘッドレス Web でテストする場合:-d web-server を指定して実行します。
  • Android(ローカル)でテストする場合:flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart を実行します。
  • Firebase Test Lab(Android)でテストする場合:
    1. デバッグ APK をビルドします:flutter build apk --debug
    2. テスト APK をビルドします:./gradlew app:assembleAndroidTest
    3. 両方の APK を Firebase Test Lab コンソールにアップロードします。

ワークフロー:エンドツーエンドの統合テスト

このチェックリストをコピーして、統合テストを実装・検証してください。

  • タスクの進捗:セットアップ
    • pubspec.yaml に integration_test と flutter_test を追加します。
    • アプリのエントリーポイントに enableFlutterDriverExtension() を組み込みます。
    • 対象ウィジェットに ValueKeys を割り当てます。
  • タスクの進捗:調査
    • MCP 経由で launch_app を実行します。
    • get_widget_tree を使ってウィジェットツリーを把握します。
    • MCP ツール(tap、enter_text)を使って操作経路を検証します。
  • タスクの進捗:作成
    • integration_test/app_test.dart を作成します。
    • WidgetTester API を使ってテストケースを記述します。
    • integrationDriver() を含む test_driver/integration_test.dart を作成します。
  • タスクの進捗:実行とフィードバックループ
    • flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart を実行します。
    • フィードバックループ:テスト出力を確認します -> PumpAndSettleTimedOutException が発生した場合は、無限アニメーションがないか確認します -> ウィジェットが見つからない場合は、scrollUntilVisible を追加します -> テストが成功するまで再実行します。

例

標準的な統合テスト(integration_test/app_test.dart)
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('End-to-end test', () {
    testWidgets('tap on the floating action button, verify counter', (tester) async {
      // Load app widget.
      await tester.pumpWidget(const MyApp());

      // Verify the counter starts at 0.
      expect(find.text('0'), findsOneWidget);

      // Find the floating action button to tap on.
      final fab = find.byKey(const ValueKey('increment'));

      // Emulate a tap on the floating action button.
      await tester.tap(fab);

      // Trigger a frame and wait for animations.
      await tester.pumpAndSettle();

      // Verify the counter increments by 1.
      expect(find.text('1'), findsOneWidget);
    });
  });
}
ホストドライバースクリプト(test_driver/integration_test.dart)
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();
パフォーマンスプロファイリング用ドライバースクリプト(test_driver/perf_driver.dart)

テスト操作を binding.traceAction() でラップしてパフォーマンス指標を取得する場合は、このドライバースクリプトを使用します。

import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() {
  return integrationDriver(
    responseDataCallback: (data) async {
      if (data != null) {
        final timeline = driver.Timeline.fromJson(
          data['scrolling_timeline'] as Map<String, dynamic>,
        );

        final summary = driver.TimelineSummary.summarize(timeline);

        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}

コメント