Testing Strategy
-
Test Pyramid: More unit and widget tests, fewer integration tests. Unit tests are fastest and cheapest.
-
Mirror Test Rule: 100% logic and widget coverage. No code without a test.
-
Mirror Organization: Test files MUST strictly mirror the lib/ directory structure and end with _test.dart .
-
Coverage Targets: Target 100% logic coverage for domain and bloc layers.
-
Test Independence: Each test MUST be independent. No shared mutable state between tests.
Unit Testing
-
Follow the Arrange-Act-Assert (Given-When-Then) convention for clear and maintainable tests
-
Use mocks for dependencies except for lightweight third-party services
-
Test business logic in isolation from the UI
-
For tests, ensure to create 5 to 8 tests for logical operations with real scenarios
-
Mocking Protocol: Use mocktail for all dependency mocking. STRICTLY prohibit using real implementation dependencies in unit tests.
-
Pure Logic: Business logic inside BLoCs or UseCases should be extracted to static pure functions for 100% unit test coverage.
Widget Testing
-
Write widget tests for all major UI components
-
Test user interactions and state changes
-
Widget Keys: Use Key('feature_action_id') format on interactive widgets for test access
-
Test Localization: Use AppLocalizations (context.l10n ) in widget tests — no hardcoded strings
Integration Testing
-
Use IntegrationTestWidgetsFlutterBinding.ensureInitialized() at the start of integration tests
-
Interact with widgets via Key (e.g., find.byKey(const ValueKey('increment')) )
-
Use pumpAndSettle() to wait for animations and async operations to complete
-
Run with: flutter test integration_test/
Test Naming & Structure
-
Test Naming: Use string interpolation for test group names: group('$ClassName', not group('ClassName', . This ensures consistency and enables better tooling support.
-
Test Grouping: Use group() to organize tests by feature, class, or state for clearer reporting.
-
Descriptive Names: Test names should clearly describe what is being tested and why.
Common Test Errors
-
A RenderFlex overflowed... — Wrap widget in Expanded or constrain dimensions in test
-
Vertical viewport was given unbounded height — Wrap ListView in SizedBox with fixed height in test
-
setState called during build — Defer state changes to post-frame callback
Running Tests
-
flutter test — Run all unit and widget tests
-
flutter test test/path/to/file_test.dart — Run specific test file
-
flutter test integration_test/ — Run integration tests
-
flutter test --coverage — Run with coverage report