flutter-add-integration-test

Configures Flutter Driver pour l'interaction avec l'application et convertit les actions MCP en tests d'intégration permanents. À utiliser lors de l'ajout de tests d'intégration à un projet, de l'exploration de composants UI via MCP, ou de l'automatisation de flux utilisateur avec le package integration_test.

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

Implémentation des tests d'intégration Flutter

Sommaire

Configuration du projet et dépendances

Configurez le projet pour supporter les tests d'intégration et les extensions Flutter Driver.

  1. Ajoutez les dépendances de développement requises à pubspec.yaml :
    flutter pub add 'dev:integration_test:{"sdk":"flutter"}'
    flutter pub add 'dev:flutter_test:{"sdk":"flutter"}'
  2. Activez l'extension Flutter Driver dans le point d'entrée de votre application (généralement lib/main.dart ou un fichier dédié lib/main_test.dart) :
    • Importez package:flutter_driver/driver_extension.dart.
    • Appelez enableFlutterDriverExtension(); avant runApp().
  3. Ajoutez des paramètres Key (par exemple, ValueKey('login_button')) aux widgets critiques du code de l'application pour assurer un ciblage fiable lors des tests.

Exploration interactive via MCP

Utilisez les outils du serveur Dart/Flutter MCP pour explorer et manipuler interactivement l'état de l'application avant de rédiger des tests statiques.

  • Lancer : Exécutez launch_app avec target: "lib/main_test.dart" pour démarrer l'application et obtenir l'URI DTD.
  • Inspecter : Exécutez get_widget_tree pour découvrir les Keys disponibles, les nœuds Text et les Types de widgets.
  • Interagir : Exécutez tap, enter_text et scroll pour simuler les flux utilisateur.
  • Attendre : Toujours exécuter waitFor ou vérifier l'état avec get_health lors de la navigation ou du déclenchement d'animations.
  • Dépanner les widgets non montés : Si un widget ne se trouve pas dans l'arborescence, il peut être chargé paresseusement dans un SliverList ou ListView. Exécutez scroll ou scrollIntoView pour forcer le widget à se monter avant d'interagir avec lui.

Directives de rédaction de tests

Structurez les tests d'intégration en utilisant le paradigme de l'API flutter_test.

  • Créez un répertoire dédié integration_test/ à la racine du projet.
  • Nommez tous les fichiers de test en utilisant la convention <name>_test.dart.
  • Initialisez la liaison en appelant IntegrationTestWidgetsFlutterBinding.ensureInitialized(); au début de main().
  • Chargez l'interface utilisateur de l'application en utilisant await tester.pumpWidget(MyApp());.
  • Déclenchez les images et attendez que les animations se terminent en utilisant await tester.pumpAndSettle(); après des interactions comme tester.tap().
  • Vérifiez la visibilité du widget en utilisant expect(find.byKey(ValueKey('foo')), findsOneWidget); ou findsNothing.
  • Faites défiler jusqu'aux widgets spécifiques hors écran en utilisant await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder);.

Logique conditionnelle pour les tests flutter_driver hérités :

  • Si vous maintenez ou migrez des tests flutter_driver hérités, utilisez driver.waitFor(), driver.waitForAbsent(), driver.tap() et driver.scroll() à la place des APIs WidgetTester.

Exécution et profilage

Exécutez les tests en utilisant la commande flutter drive. Nécessite un script pilote hôte situé dans test_driver/integration_test.dart qui appelle integrationDriver().

Cibles d'exécution conditionnelle :

  • Si test sur Chrome : Lancez chromedriver --port=4444 dans un terminal séparé, puis exécutez : flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome
  • Si test web sans interface : Exécutez avec -d web-server.
  • Si test sur Android (Local) : Exécutez flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart.
  • Si test sur Firebase Test Lab (Android) :
    1. Construisez APK de débogage : flutter build apk --debug
    2. Construisez APK de test : ./gradlew app:assembleAndroidTest
    3. Téléchargez les deux APK sur la console Firebase Test Lab.

Flux de travail : tests d'intégration de bout en bout

Copiez et suivez cette liste de contrôle pour implémenter et vérifier les tests d'intégration.

  • [ ] Progression des tâches : Configuration
    • [ ] Ajoutez integration_test et flutter_test à pubspec.yaml.
    • [ ] Injectez enableFlutterDriverExtension() dans le point d'entrée de l'application.
    • [ ] Attribuez des ValueKeys aux widgets cibles.
  • [ ] Progression des tâches : Exploration
    • [ ] Exécutez launch_app via MCP.
    • [ ] Cartographiez l'arborescence des widgets en utilisant get_widget_tree.
    • [ ] Validez les chemins d'interaction en utilisant les outils MCP (tap, enter_text).
  • [ ] Progression des tâches : Rédaction
    • [ ] Créez integration_test/app_test.dart.
    • [ ] Rédigez des cas de test en utilisant les APIs WidgetTester.
    • [ ] Créez test_driver/integration_test.dart avec integrationDriver().
  • [ ] Progression des tâches : Exécution et boucle de rétroaction
    • [ ] Exécutez flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart.
    • [ ] Boucle de rétroaction : Examinez la sortie du test -> Si PumpAndSettleTimedOutException se produit, vérifiez les animations infinies -> Si le widget n'est pas trouvé, ajoutez scrollUntilVisible -> Réexécutez le test jusqu'à ce qu'il réussisse.

Exemples

Test d'intégration standard (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);
    });
  });
}

Script pilote hôte (test_driver/integration_test.dart)

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

Script pilote de profilage des performances (test_driver/perf_driver.dart)

Utilisez ce script pilote si vous enveloppez vos actions de test dans binding.traceAction() pour capturer les métriques de performance.

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,
        );
      }
    },
  );
}