284 lines
6.8 KiB
Markdown
284 lines
6.8 KiB
Markdown
# 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:**
|
|
```bash
|
|
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:**
|
|
```dart
|
|
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:**
|
|
```java
|
|
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:**
|
|
```dart
|
|
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:**
|
|
```dart
|
|
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:**
|
|
```dart
|
|
@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:**
|
|
```bash
|
|
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:**
|
|
```dart
|
|
test('getPlatformVersion', () async {
|
|
expect(await arcPlugin.getPlatformVersion(), '42');
|
|
});
|
|
```
|
|
|
|
**Platform Instance Mocking:**
|
|
```dart
|
|
test('methodName', () async {
|
|
Arc arcPlugin = Arc();
|
|
MockArcPlatform fakePlatform = MockArcPlatform();
|
|
ArcPlatform.instance = fakePlatform; // Override instance
|
|
|
|
expect(await arcPlugin.method(), expectedResult);
|
|
});
|
|
```
|
|
|
|
**Setup/Teardown:**
|
|
```dart
|
|
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:
|
|
```dart
|
|
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* |