Back to list
KitfoxPilot

flutter-testing

by KitfoxPilot

0🍴 0📅 Jan 25, 2026

SKILL.md


name: flutter-testing description: Flutter testing patterns for aviation EFB applications. Use when writing unit tests, widget tests, integration tests, or golden tests. Includes mocking strategies for location/sensors, test fixtures for aviation data, and patterns for testing BLoC state management, offline scenarios, and safety-critical navigation calculations.

Flutter Testing

Overview

Comprehensive testing patterns for MagentaLine EFB, ensuring reliability for safety-critical aviation software.

Test Types

TypeLocationPurposeSpeed
Unittest/Pure Dart logic, calculationsFast
Widgettest/UI components in isolationFast
Integrationintegration_test/Full app flowsSlow
Goldentest/Visual regressionMedium

Quick Start

Run All Tests

flutter test
flutter test --coverage

Run Specific Test

flutter test test/domain/navigation_test.dart

Integration Tests

flutter test integration_test/app_test.dart

Unit Testing Aviation Calculations

Critical: All navigation math must have 100% test coverage.

import 'package:flutter_test/flutter_test.dart';
import 'package:magentaline/domain/navigation/haversine.dart';

void main() {
  group('Haversine Distance', () {
    test('KLAX to KSFO is ~300nm', () {
      final distance = haversineNm(
        lat1: 33.9425, lon1: -118.4081,  // KLAX
        lat2: 37.6213, lon2: -122.3790,  // KSFO
      );
      expect(distance, closeTo(300, 5));
    });

    test('same point returns zero', () {
      expect(haversineNm(lat1: 0, lon1: 0, lat2: 0, lon2: 0), equals(0));
    });

    test('antipodal points return half circumference', () {
      final distance = haversineNm(lat1: 0, lon1: 0, lat2: 0, lon2: 180);
      expect(distance, closeTo(10800, 10)); // ~180° of great circle
    });
  });
}

Widget Testing

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:magentaline/presentation/widgets/altitude_tape.dart';

void main() {
  testWidgets('AltitudeTape displays current altitude', (tester) async {
    await tester.pumpWidget(
      const MaterialApp(
        home: AltitudeTape(altitude: 5500, verticalSpeed: 500),
      ),
    );

    expect(find.text('5500'), findsOneWidget);
    expect(find.byIcon(Icons.arrow_upward), findsOneWidget);
  });

  testWidgets('AltitudeTape shows descent indicator', (tester) async {
    await tester.pumpWidget(
      const MaterialApp(
        home: AltitudeTape(altitude: 3000, verticalSpeed: -800),
      ),
    );

    expect(find.byIcon(Icons.arrow_downward), findsOneWidget);
  });
}

BLoC Testing

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockAirportRepository extends Mock implements AirportRepository {}

void main() {
  late MockAirportRepository mockRepository;

  setUp(() {
    mockRepository = MockAirportRepository();
  });

  group('AirportSearchBloc', () {
    blocTest<AirportSearchBloc, AirportSearchState>(
      'emits [loading, loaded] when search succeeds',
      build: () {
        when(() => mockRepository.search('KLAX'))
            .thenAnswer((_) async => [testAirport]);
        return AirportSearchBloc(repository: mockRepository);
      },
      act: (bloc) => bloc.add(const SearchAirports('KLAX')),
      expect: () => [
        const AirportSearchLoading(),
        AirportSearchLoaded([testAirport]),
      ],
    );

    blocTest<AirportSearchBloc, AirportSearchState>(
      'emits [loading, error] when search fails',
      build: () {
        when(() => mockRepository.search(any()))
            .thenThrow(Exception('Network error'));
        return AirportSearchBloc(repository: mockRepository);
      },
      act: (bloc) => bloc.add(const SearchAirports('KLAX')),
      expect: () => [
        const AirportSearchLoading(),
        isA<AirportSearchError>(),
      ],
    );
  });
}

Mocking Sensors & Location

See references/mocking_sensors.md for comprehensive mocking patterns.

// Mock GPS position stream
class MockLocationService extends Mock implements LocationService {}

void main() {
  late MockLocationService mockLocation;

  setUp(() {
    mockLocation = MockLocationService();
    when(() => mockLocation.positionStream).thenAnswer(
      (_) => Stream.fromIterable([
        Position(latitude: 33.9425, longitude: -118.4081, altitude: 1000),
        Position(latitude: 33.9500, longitude: -118.4000, altitude: 1500),
      ]),
    );
  });
}

Golden Tests

import 'package:golden_toolkit/golden_toolkit.dart';

void main() {
  testGoldens('FlightInstruments renders correctly', (tester) async {
    await loadAppFonts();

    final builder = DeviceBuilder()
      ..overrideDevicesForAllScenarios(devices: [Device.phone, Device.tablet])
      ..addScenario(
        name: 'day_mode',
        widget: const FlightInstruments(theme: EfbTheme.day),
      )
      ..addScenario(
        name: 'night_mode',
        widget: const FlightInstruments(theme: EfbTheme.night),
      );

    await tester.pumpDeviceBuilder(builder);
    await screenMatchesGolden(tester, 'flight_instruments');
  });
}

Test Fixtures

Use references/test_fixtures.md for aviation test data patterns.

// test/fixtures/airports.dart
final testKLAX = Airport(
  id: 'KLAX',
  name: 'Los Angeles International',
  latitude: 33.9425,
  longitude: -118.4081,
  elevation: 128,
  runways: [testRunway25L, testRunway25R],
);

Integration Testing

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

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('complete flight planning flow', (tester) async {
    app.main();
    await tester.pumpAndSettle();

    // Search for departure airport
    await tester.tap(find.byKey(const Key('departure_field')));
    await tester.enterText(find.byKey(const Key('departure_field')), 'KLAX');
    await tester.pumpAndSettle();
    await tester.tap(find.text('Los Angeles International'));
    await tester.pumpAndSettle();

    // Search for destination
    await tester.tap(find.byKey(const Key('destination_field')));
    await tester.enterText(find.byKey(const Key('destination_field')), 'KSFO');
    await tester.pumpAndSettle();
    await tester.tap(find.text('San Francisco International'));
    await tester.pumpAndSettle();

    // Verify route calculated
    expect(find.textContaining('300 nm'), findsOneWidget);
  });
}

Coverage Requirements

For aviation safety-critical code:

ComponentMinimum Coverage
Navigation math100%
Flight planning95%
Data parsing90%
UI widgets80%
Overall85%
# Generate coverage report
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html

References

DocumentDescription
references/mocking_sensors.mdGPS, compass, barometer mocking
references/test_fixtures.mdAviation data fixtures
references/bloc_testing.mdBLoC test patterns
references/golden_testing.mdVisual regression setup

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon