Files
ch34/.planning/codebase/TESTING.md
2026-04-21 12:57:40 +08:00

6.8 KiB

Testing Patterns

Analysis Date: 2026-04-16

Framework

Runner:

  • flutter_test (Flutter SDK built-in)
  • integration_test (Flutter SDK built-in, for E2E/integration tests)
  • No explicit test package dependency (uses Flutter's bundled test framework)

Assertion Library:

  • Built-in flutter_test matchers (expect, findsOneWidget, isEmpty, isInstanceOf)

Dependencies:

  • flutter_test (sdk: flutter) - Unit and widget testing
  • flutter_lints (^3.0.0) - Code analysis
  • plugin_platform_interface (^2.0.2) - Required for mock platform interface mixin

Run Commands:

flutter test                          # Run all unit tests
flutter test --coverage              # Run tests with coverage
flutter test test/ch34_test.dart     # Run a specific test file
flutter test integration_test/       # Run integration tests (from example/)

Test Structure

Test File Organization:

  • Plugin tests in D:/code/new_git_code/flutter/ch34/test/:
    • ch34_test.dart - Unit tests with mock platform
    • ch34_method_channel_test.dart - MethodChannel-level tests with mock handler
  • Example app tests in D:/code/new_git_code/flutter/ch34/example/test/:
    • widget_test.dart - Widget tests for example app
  • Integration tests in D:/code/new_git_code/flutter/ch34/example/integration_test/:
    • plugin_integration_test.dart - End-to-end plugin tests on real device

Naming:

  • Test files: {source_file}_test.dart pattern
  • Integration test files: {feature}_integration_test.dart

Test Entry Pattern: All tests call framework initialization before running:

// Unit tests
TestWidgetsFlutterBinding.ensureInitialized();

// Integration tests
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

Suite Organization: Unit tests follow a consistent pattern:

// D:/code/new_git_code/flutter/ch34/test/ch34_test.dart
void main() {
  MethodChannelCh34.registerDefault();
  final Ch34Platform initialPlatform = Ch34Platform.instance;

  test('$MethodChannelCh34 is the default instance', () {
    expect(initialPlatform, isInstanceOf<MethodChannelCh34>());
  });

  test('getPlatformVersion', () async {
    MockCh34Platform fakePlatform = MockCh34Platform();
    Ch34Platform.instance = fakePlatform;

    expect(await Ch34Manager.getPlatformVersion(), '42');
  });

  test('enumDevice returns empty list', () async {
    MockCh34Platform fakePlatform = MockCh34Platform();
    Ch34Platform.instance = fakePlatform;

    final devices = await Ch34Manager.enumDevice();
    expect(devices, isEmpty);
  });
}

Mocking

Framework: Manual mock classes (no mockito or mocktail dependency)

Platform Mock Pattern: D:/code/new_git_code/flutter/ch34/test/ch34_test.dart uses MockPlatformInterfaceMixin for creating test doubles:

class MockCh34Platform
    with MockPlatformInterfaceMixin
    implements Ch34Platform {

  @override
  Future<String?> getPlatformVersion() => Future.value('42');

  @override
  Future<List<UsbDeviceInfo>> enumDevice() => Future.value([]);

  // ... all 30+ methods must be overridden
}

Mock Usage Pattern: Mock instances are swapped into the platform interface before each test:

MockCh34Platform fakePlatform = MockCh34Platform();
Ch34Platform.instance = fakePlatform;

expect(await Ch34Manager.getPlatformVersion(), '42');

MethodChannel Mock Pattern: D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart uses setMockMethodCallHandler for low-level channel mocking:

const MethodChannel channel = MethodChannel('ch34');

setUp(() {
  TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
      .setMockMethodCallHandler(
    channel,
    (MethodCall methodCall) async {
      return '42';  // Returns same value for all method calls
    },
  );
});

tearDown(() {
  TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
      .setMockMethodCallHandler(channel, null);
});

What to Mock:

  • Platform interface layer (Ch34Platform) for testing Ch34Manager delegation
  • MethodChannel for testing MethodChannelCh34 channel mapping

What NOT to Mock:

  • Native Android code (tested via integration tests on real device)
  • EventChannel streams (not currently mocked in any test)

Coverage

Requirements: No coverage target enforced

Current State:

  • Only 2 unit test assertions beyond boilerplate (getPlatformVersion, enumDevice returns empty list)
  • MethodChannel test has only 1 assertion (getPlatformVersion)
  • Integration test has 1 assertion (getPlatformVersion on real device)
  • Most platform methods have zero test coverage (GPIO, serial I/O, modem status, signal control, etc.)

View Coverage:

flutter test --coverage
# Coverage written to coverage/lcov.info

Test Locations

Unit Tests:

  • D:/code/new_git_code/flutter/ch34/test/ch34_test.dart - Ch34Manager + mock platform
  • D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart - MethodChannel tests

Integration Tests:

  • D:/code/new_git_code/flutter/ch34/example/integration_test/plugin_integration_test.dart - Real device tests

Widget Tests:

  • D:/code/new_git_code/flutter/ch34/example/test/widget_test.dart - Example app widget test (currently outdated - looks for "Running on:" text that no longer exists)

Test Types

Unit Tests:

  • Scope: Test Ch34Manager delegation to mocked platform
  • Approach: Replace Ch34Platform.instance with MockCh34Platform
  • Current coverage: 2 methods tested out of 30+ API methods

Integration Tests:

  • Framework: integration_test package
  • Approach: Run on real Android device with actual USB hardware
  • Current coverage: 1 test (getPlatformVersion)

E2E Tests:

  • No dedicated E2E test framework beyond integration_test

Common Patterns

Async Testing: All platform method tests use async/await:

test('enumDevice returns empty list', () async {
  MockCh34Platform fakePlatform = MockCh34Platform();
  Ch34Platform.instance = fakePlatform;

  final devices = await Ch34Manager.enumDevice();
  expect(devices, isEmpty);
});

Error Testing: Mock throws exceptions to test error paths:

@override
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex) =>
    throw const Ch34Exception('Not supported');

Setup/Teardown Pattern:

setUp(() {
  // Configure mock method channel handler
  TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
      .setMockMethodCallHandler(channel, handler);
});

tearDown(() {
  // Clean up mock handler
  TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
      .setMockMethodCallHandler(channel, null);
});

CI Testing

CI Pipeline: Not detected - no .github/workflows/, .gitlab-ci.yml, or azure-pipelines.yml found.

Recommendation: Tests should be run via flutter analyze && flutter test in any CI pipeline.


Testing analysis: 2026-04-16