DEV Community

kanta13jp1
kanta13jp1

Posted on

Flutter Testing Strategy: Widget, Integration, and Golden Tests

Flutter Testing Strategy: Widget, Integration, and Golden Tests

Picking the right test type keeps CI fast without sacrificing confidence.

Test Selection Guide

Business logic alone      → Unit test
UI component              → Widget test
Screen navigation / DB    → Integration test
Design regression         → Golden test
Enter fullscreen mode Exit fullscreen mode

Unit Tests: The Foundation

// test/services/score_calculator_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/services/score_calculator.dart';

void main() {
  group('ScoreCalculator', () {
    test('returns 0 for empty list', () {
      expect(ScoreCalculator.total([]), equals(0));
    });

    test('sums positive values', () {
      expect(ScoreCalculator.total([10, 20, 30]), equals(60));
    });

    test('ignores negative values', () {
      expect(ScoreCalculator.total([10, -5, 20]), equals(30));
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

Widget Tests: UI Component Verification

// test/widgets/task_card_test.dart
testWidgets('TaskCard shows title and completion button', (tester) async {
  bool completed = false;

  await tester.pumpWidget(
    MaterialApp(
      home: Scaffold(
        body: TaskCard(
          title: 'Test task',
          onComplete: () => completed = true,
        ),
      ),
    ),
  );

  expect(find.text('Test task'), findsOneWidget);
  expect(find.byIcon(Icons.check_circle_outline), findsOneWidget);

  await tester.tap(find.byIcon(Icons.check_circle_outline));
  expect(completed, isTrue);
});

testWidgets('TaskCard shows completed state', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: Scaffold(
        body: TaskCard(
          title: 'Done task',
          isCompleted: true,
          onComplete: () {},
        ),
      ),
    ),
  );

  expect(find.byIcon(Icons.check_circle), findsOneWidget);
});
Enter fullscreen mode Exit fullscreen mode

Integration Tests: Full Flow with Supabase

// integration_test/task_flow_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Login → create task → complete flow', (tester) async {
    app.main();
    await tester.pumpAndSettle();

    await tester.enterText(find.byKey(const Key('email')), 'test@example.com');
    await tester.enterText(find.byKey(const Key('password')), 'password123');
    await tester.tap(find.text('Login'));
    await tester.pumpAndSettle(const Duration(seconds: 3));

    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();
    await tester.enterText(find.byKey(const Key('task_title')), 'Integration test task');
    await tester.tap(find.text('Save'));
    await tester.pumpAndSettle(const Duration(seconds: 2));

    expect(find.text('Integration test task'), findsOneWidget);
  });
}
Enter fullscreen mode Exit fullscreen mode

Golden Tests: Design Regression Detection

// test/golden/task_card_golden_test.dart
testWidgets('TaskCard golden test', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      theme: ThemeData.dark(),
      home: const Scaffold(
        body: Center(
          child: TaskCard(title: 'Golden test', isCompleted: false),
        ),
      ),
    ),
  );

  // First run: flutter test --update-goldens to create snapshot
  await expectLater(
    find.byType(TaskCard),
    matchesGoldenFile('goldens/task_card.png'),
  );
});
Enter fullscreen mode Exit fullscreen mode

CI Configuration (GHA)

- name: Flutter test (unit + widget)
  run: flutter test test/ --coverage

- name: Integration test (Chrome)
  run: |
    flutter drive \
      --driver=test_driver/integration_test.dart \
      --target=integration_test/task_flow_test.dart \
      -d chrome

- name: Upload coverage
  uses: codecov/codecov-action@v4
  with:
    file: coverage/lcov.info
Enter fullscreen mode Exit fullscreen mode

Summary

Unit          → logic only · fast · write many
Widget        → UI components · medium speed · cover key widgets
Integration   → E2E golden path · slow · 1–3 scenarios max
Golden        → design regression · regenerate with --update-goldens
Enter fullscreen mode Exit fullscreen mode

Aim for 70% unit / 25% widget / 5% integration. Writing everything at the
same depth makes CI unbearably slow — pick your battles.

Top comments (0)