flutter-widget-testing
SKILL.md
When to Use
- Testing UI components (atoms, molecules, organisms, templates, pages)
- Testing widget rendering, layout, and text content
- Testing user interactions (tap, scroll, enter text, drag)
- Creating golden tests for visual regression
- Verifying accessibility (semantics, labels)
- Testing widgets that use Riverpod providers
- Testing navigation and routing behavior
Critical Patterns
| Pattern | Rule |
|---|---|
| Test FIRST | Write the failing widget test before the widget. Red -> Green -> Refactor. |
| Wrap with app shell | Always wrap tested widgets in MaterialApp or the Forui theme equivalent |
| pump vs pumpAndSettle | pump() advances one frame; pumpAndSettle() waits for all animations |
| Finders are lazy | find.text('X') doesn't search until used in expect() or tester.tap() |
| Golden tests pinned | Update goldens explicitly with --update-goldens; never auto-update in CI |
| Test behavior, not pixels | Prefer semantic finders (find.text, find.byType) over pixel coordinates |
| Riverpod: use ProviderScope | Wrap widget in ProviderScope with overrides for test isolation |
Test Directory Structure
mobile/
test/
shared/
presentation/
atoms/
app_button_test.dart
app_text_field_test.dart
app_badge_test.dart
molecules/
emphasis_headline_test.dart
organisms/
pet_emoji_carousel_test.dart
features/
auth/
presentation/
pages/
welcome_page_test.dart
widgets/
auth_sheet_test.dart
goldens/ # Golden reference images
shared/
atoms/
app_button_light.png
app_button_dark.png
features/
auth/
welcome_page_light.png
helpers/
pump_app.dart # Shared widget wrapper helper
test_theme.dart # Theme setup for tests
Widget Test Wrapper Helper
Every widget test needs a proper app shell with theme. Create a shared helper:
Atom Tests (Simple Widgets)
Text Field Tests
Interaction Tests
Page Tests with Riverpod
Navigation Tests
Golden Tests (Visual Regression)
Accessibility Tests
Finders Reference
| Finder | Use For |
|---|---|
find.text('X') |
Find widget displaying exact text |
find.textContaining('X') |
Find widget containing text |
find.byType(AppButton) |
Find widget by runtime type |
find.byKey(Key('x')) |
Find widget by key |
find.byIcon(Icons.home) |
Find widget by icon |
find.descendant(of: x, matching: y) |
Find widget nested inside another |
find.ancestor(of: x, matching: y) |
Find parent widget |
find.byWidgetPredicate((w) => ...) |
Custom predicate |
find.bySemanticsLabel('X') |
Find by accessibility label |
Matchers Reference
| Matcher | Verifies |
|---|---|
findsOneWidget |
Exactly one match |
findsNothing |
No matches |
findsNWidgets(n) |
Exactly N matches |
findsAtLeastNWidgets(n) |
At least N matches |
matchesGoldenFile('path') |
Visual match against golden |
Commands
# Run all widget tests
flutter test
# Run specific widget test
flutter test test/shared/presentation/atoms/app_button_test.dart
# Update golden files (run locally, commit the PNGs)
flutter test --update-goldens
# Update specific golden
flutter test --update-goldens test/shared/presentation/atoms/app_button_golden_test.dart
# Run with coverage
flutter test --coverage
# Run tests matching name
flutter test --name "AppButton"
Dev Dependencies
Anti-Patterns
| Don't | Do |
|---|---|
| Test without app shell/theme | Always use pumpApp() helper with Forui theme |
Use find.byType(Text) broadly |
Use find.text('specific text') for precision |
Forget pump() after interactions |
Always pump() or pumpAndSettle() after tap/input |
| Hardcode pixel positions | Use semantic finders |
| Update goldens in CI | Only update locally, review diffs, commit PNGs |
| Test Forui internals | Test YOUR widget's behavior, not Forui's |
| Skip dark mode tests | Test both light and dark themes |
| Forget to dispose controllers | Use addTeardown or dispose in tearDown |
Weekly Installs
1
Repository
333-333-333/agentsFirst Seen
3 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
kiro-cli1