233 lines
6.8 KiB
Markdown
233 lines
6.8 KiB
Markdown
# 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:**
|
|
```bash
|
|
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:
|
|
```dart
|
|
// Unit tests
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// Integration tests
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
```
|
|
|
|
**Suite Organization:**
|
|
Unit tests follow a consistent pattern:
|
|
|
|
```dart
|
|
// 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:
|
|
|
|
```dart
|
|
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:
|
|
```dart
|
|
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:
|
|
|
|
```dart
|
|
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:**
|
|
```bash
|
|
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`:
|
|
```dart
|
|
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:
|
|
```dart
|
|
@override
|
|
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex) =>
|
|
throw const Ch34Exception('Not supported');
|
|
```
|
|
|
|
**Setup/Teardown Pattern:**
|
|
```dart
|
|
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*
|