Files
arc/.planning/codebase/TESTING.md
2026-03-30 15:21:07 +08:00

6.8 KiB

Testing Patterns

Analysis Date: 2026-03-30

Test Framework

Runner:

  • Flutter Test Framework (flutter_test SDK)
  • Dart SDK: ^3.9.0
  • Config: analysis_options.yaml (linting only, no test config file)

Assertion Library:

  • flutter_test package: expect(), findsOneWidget, isInstanceOf<T>()

Run Commands:

flutter test                         # Run all tests
flutter test test/arc_test.dart      # Run specific test file
flutter test --coverage              # Run with coverage

Test File Organization

Location:

  • Plugin tests: test/ directory (co-located with lib)
  • Example tests: example/test/ directory
  • Java tests: android/src/test/java/

Naming:

  • Dart: *_test.dart pattern (arc_test.dart, arc_method_channel_test.dart, widget_test.dart)
  • Java: *Test.java pattern (ArcPluginTest.java)

Structure:

arc/
├── test/
│   ├── arc_test.dart              # Platform interface tests
│   └── arc_method_channel_test.dart # Method channel tests
├── example/
│   └── test/
│       └── widget_test.dart       # Widget tests
└── android/
    └── src/
        └── test/
            └── java/
                └── com/xiarui/arc/
                    └── ArcPluginTest.java

Test Structure

Dart Unit Tests:

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  final ArcPlatform initialPlatform = ArcPlatform.instance;

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

  test('getPlatformVersion', () async {
    Arc arcPlugin = Arc();
    MockArcPlatform fakePlatform = MockArcPlatform();
    ArcPlatform.instance = fakePlatform;

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

Java Unit Tests:

public class ArcPluginTest {
  @Test
  public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
    ArcPlugin plugin = new ArcPlugin();

    final MethodCall call = new MethodCall("getPlatformVersion", null);
    MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
    plugin.onMethodCall(call, mockResult);

    verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
  }
}

Mocking

Dart Mocking:

  • MockPlatformInterfaceMixin from plugin_platform_interface
  • Custom mock class implementing platform interface

Example:

class MockArcPlatform
    with MockPlatformInterfaceMixin
    implements ArcPlatform {

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

  @override
  Future<Map<String, dynamic>?> activeOnline({
    required String appId,
    required String sdkKey,
    required String activeKey,
  }) => Future.value({'success': true, 'errorCode': 0, 'message': 'success'});
}

Method Channel Mocking:

setUp(() {
  TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
    channel,
    (MethodCall methodCall) async {
      return '42';
    },
  );
});

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

Java Mocking:

  • Mockito framework
  • mock(Class.class) for mock objects
  • verify(mock).method(args) for verification

What to Mock:

  • Platform interfaces for unit testing
  • Method channels for integration testing
  • Native engine calls

What NOT to Mock:

  • Data transformation logic (test directly)
  • Simple utility functions

Fixtures and Factories

Test Data:

  • Mock return values defined inline
  • Simple string/int values: '42', 0, true

Example Mock Data:

@override
Future<Map<String, dynamic>?> detectFaces({
  required Uint8List data,
  required int width,
  required int height,
  int format = 2050,
}) => Future.value({
  'success': true,
  'errorCode': 0,
  'faceList': [],
  'rgbLiveness': 1,
  'isRgbAlive': true
});

Location:

  • Mock implementations in test files
  • No separate fixture files

Coverage

Requirements: None enforced

View Coverage:

flutter test --coverage
genhtml coverage/lcov.info -o coverage/html

Test Types

Unit Tests:

  • Platform interface instantiation
  • Method channel default instance verification
  • Mock platform behavior testing
  • File: test/arc_test.dart, test/arc_method_channel_test.dart

Integration Tests:

  • Method channel handler testing
  • Native plugin method call verification
  • File: android/src/test/java/com/xiarui/arc/ArcPluginTest.java

Widget Tests:

  • Basic widget rendering verification
  • File: example/test/widget_test.dart

E2E Tests: Not used

Common Patterns

Async Testing:

test('getPlatformVersion', () async {
  expect(await arcPlugin.getPlatformVersion(), '42');
});

Platform Instance Mocking:

test('methodName', () async {
  Arc arcPlugin = Arc();
  MockArcPlatform fakePlatform = MockArcPlatform();
  ArcPlatform.instance = fakePlatform;  // Override instance

  expect(await arcPlugin.method(), expectedResult);
});

Setup/Teardown:

setUp(() {
  // Initialize test binding, set up mocks
  TestWidgetsFlutterBinding.ensureInitialized();
});

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

Error Testing: Current tests do not explicitly test error scenarios. Recommended pattern for future:

test('throws on invalid params', () async {
  expect(
    () => arcPlugin.detectFaces(data: Uint8List(0), width: 0, height: 0),
    throwsA(isA<PlatformException>()),
  );
});

Test Naming Convention

Dart:

  • Simple descriptive names: 'getPlatformVersion'
  • Interpolated class names: '$MethodChannelArc is the default instance'

Java:

  • Pattern: methodName_scenario_expectedResult
  • Example: onMethodCall_getPlatformVersion_returnsExpectedValue

Key Test Files

Plugin Core Tests:

  • test/arc_test.dart - Platform interface and Arc class tests
  • test/arc_method_channel_test.dart - Method channel implementation tests

Example App Tests:

  • example/test/widget_test.dart - Basic widget test (template, needs updating)

Native Tests:

  • android/src/test/java/com/xiarui/arc/ArcPluginTest.java - Android plugin tests

Test Coverage Gaps

Untested Methods:

  • activeOnline() - No test coverage
  • init() - No test coverage
  • detectFaces() - No test coverage
  • extractFaceFeature() - No test coverage
  • compareFaceFeature() - No test coverage
  • registerFaceFeature() - No test coverage
  • registerFaceFeatureBatch() - No test coverage

Untested Scenarios:

  • Error handling (invalid parameters)
  • Edge cases (null data, empty arrays)
  • RGB liveness detection flow
  • Face feature extraction and comparison

Testing analysis: 2026-03-30