mcpField
F
flutter-add-integration-test

flutter-add-integration-test

github.com/dhruvanbhalaraTesting & quality3050 installsUpdated 9w ago
Repository

About flutter-add-integration-test

flutter-add-integration-test is a skill that guides developers through adding and running Flutter integration tests. It covers test setup and authoring, device and web targets, performance timelines, and CI workflows.

Key features

  1. 01Set up integration test dependencies
  2. 02Write widget interaction and assertions
  3. 03Run tests on devices and web
  4. 04Capture performance timelines
  5. 05Configure CI integration tests

Use cases

  1. 01Test an app flow across screens
  2. 02Verify interactions with keyed widgets
  3. 03Run integration tests in Chrome
  4. 04Profile test actions

Install

Copy the command and run it in a terminal.

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

After installing, type/flutter-add-integration-testin Claude Code to confirm it loaded.

Details

From the project README

Contents

Project Setup

  1. Add required development dependencies to pubspec.yaml:

    flutter pub add 'dev:integration_test:{"sdk":"flutter"}'
    flutter pub add 'dev:flutter_test:{"sdk":"flutter"}'
    
  2. Create directory structure:

    project_root/
    ├── integration_test/
    │   └── app_test.dart          # Test cases
    └── test_driver/
        └── integration_test.dart  # Host driver script
    
  3. Create the host driver script at test_driver/integration_test.dart:

    import 'package:integration_test/integration_test_driver.dart';
    
    Future<void> main() => integrationDriver();
    
  4. Add ValueKeys to critical widgets in production code for reliable targeting:

    FloatingActionButton(
      key: const ValueKey('increment_fab'),
      onPressed: _increment,
      child: const Icon(Icons.add),
    )
    

Test Authoring

  • Initialize the binding at the top of main(): this replaces the default test binding.
  • Load the full application with tester.pumpWidget(const MyApp()).
  • Use tester.pumpAndSettle() after every interaction to wait for animations and async operations.
  • Assert widget visibility using expect(find.byKey(ValueKey('foo')), findsOneWidget).
  • Scroll to off-screen widgets using tester.scrollUntilVisible(finder, 500.0).
Test File Structure
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('complete user flow', (tester) async {
      // Load full app
      await tester.pumpWidget(const MyApp());

      // Interact with widgets
      await tester.tap(find.byKey(const ValueKey('login_button')));
      await tester.pumpAndSettle();

      // Assert navigation happened
      expect(find.byType(HomePage), findsOneWidget);
    });
  });
}

Execution Targets

Choose the execution method based on target platform:

Local Device (Android/iOS)
flutter test integration_test/

For iOS, when running under the default Swift Package Manager (SPM) integration (Flutter 3.44+), ensure you run package resolution first:

xcodebuild -resolvePackageDependencies -workspace ios/Runner.xcworkspace -scheme Runner
Chrome (Web)
# Terminal 1: Start ChromeDriver
chromedriver --port=4444

# Terminal 2: Run tests
flutter drive \
  --driver=test_driver/integration_test.dart \
  --target=integration_test/app_test.dart \
  -d chrome
Headless Web
flutter drive \
  --driver=test_driver/integration_test.dart \
  --target=integration_test/app_test.dart \
  -d web-server
Firebase Test Lab (Android)
# 1. Build debug APK
flutter build apk --debug

# 2. Build instrumentation test APK
pushd android && ./gradlew app:assembleAndroidTest && popd

# 3. Upload both APKs to Firebase Test Lab via console or gcloud:
gcloud firebase test android run \
  --type instrumentation \
  --app build/app/outputs/flutter-apk/app-debug.apk \
  --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk

Performance Profiling

Wrap test actions in binding.traceAction() to capture performance timelines:

void main() {
  final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('scrolling performance', (tester) async {
    await tester.pumpWidget(const MyApp());

    await binding.traceAction(() async {
      final listFinder = find.byType(Scrollable);
      await tester.fling(listFinder, const Offset(0, -500), 10000);
      await tester.pumpAndSettle();
    }, reportKey: 'scrolling_timeline');
  });
}
Performance Profiling Driver

Use this driver to capture and write timeline data:

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

CI/CD Integration

GitHub Actions Workflow
- name: Run integration tests
  uses: reactivecircus/android-emulator-runner@v2
  with:
    api-level: 33
    script: flutter test integration_test/ --flavor dev
  • Use reactivecircus/android-emulator-runner for Android emulator.
  • Collect test result artifacts with actions/upload-artifact.
  • For web, run chromedriver as a service and test with -d chrome.

Common Pitfalls

ErrorCauseFix
PumpAndSettleTimedOutExceptionInfinite animation (e.g., CircularProgressIndicator)Use pump() instead, or dismiss the loading state
Widget not foundLazy-loaded in SliverList or ListViewCall scrollUntilVisible() before interacting
Test hangsNetwork call in production codeMock HTTP client or use --dart-define to bypass
No host driver specifiedMissing test_driver/integration_test.dartCreate the host driver file

Workflow: Adding an Integration Test

Task Progress
  • Step 1: Add integration_test and flutter_test to dev_dependencies.
  • Step 2: Assign ValueKeys to target widgets in production code.
  • Step 3: Create integration_test/app_test.dart with binding initialization.
  • Step 4: Create test_driver/integration_test.dart with integrationDriver().
  • Step 5: Write test cases: load app, interact, assert.
  • Step 6: Choose execution target:
    • Local device: flutter test integration_test/
    • Chrome: flutter drive ... -d chrome
    • Firebase Test Lab: build + upload APKs
  • Step 7: Feedback Loop:
    • If PumpAndSettleTimedOutException → check for infinite animations.
    • If widget not found → add scrollUntilVisible.
    • Re-run until all tests pass.

Examples

Standard Integration Test
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('Counter app', () {
    testWidgets('tap FAB, verify counter increments', (tester) async {
      await tester.pumpWidget(const MyApp());

      // Verify initial state
      expect(find.text('0'), findsOneWidget);

      // Tap the increment button
      final fab = find.byKey(const ValueKey('increment_fab'));
      await tester.tap(fab);
      await tester.pumpAndSettle();

      // Verify counter incremented
      expect(find.text('1'), findsOneWidget);
    });
  });
}
Multi-Screen Navigation Flow
testWidgets('login and navigate to home', (tester) async {
  await tester.pumpWidget(const MyApp());

  // Enter credentials
  await tester.enterText(find.byKey(const ValueKey('email_field')), '[email protected]');
  await tester.enterText(find.byKey(const ValueKey('password_field')), 'password123');

  // Submit login
  await tester.tap(find.byKey(const ValueKey('login_button')));
  await tester.pumpAndSettle();

  // Verify navigation to home
  expect(find.byType(HomePage), findsOneWidget);
  expect(find.byType(LoginPage), findsNothing);
});

Comments

Markdown supported