Install
$ agentstack add skill-iml1s-flutter-claude-skills-flutter-integration-testing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Flutter Integration Testing That Catches Real Bugs
Core Principle
> Integration tests verify that components work TOGETHER correctly. > Unit tests prove functions work in isolation. Integration tests prove THE SYSTEM works.
When to Use Integration vs Unit Tests
| Use Unit Test | Use Integration Test | |---------------|---------------------| | Pure logic functions | Widget renders correctly with provider data | | Data transformations | Navigation between pages works | | State machine transitions | User interaction flows (tap → state change → UI update) | | Error message formatting | Multiple providers interacting correctly | | Single provider logic | Form submission → validation → API → result |
Flutter-Specific Patterns
Pattern 1: Widget Test with Real Providers
testWidgets('favorite button toggles and persists', (tester) async {
final fakePrefs = FakeSharedPreferences();
await tester.pumpWidget(
ProviderScope(
overrides: [
sharedPreferencesProvider.overrideWithValue(fakePrefs),
],
child: MaterialApp(home: StopDetailPage(stop: testStop)),
),
);
// Verify initial state: not favorited
expect(find.byIcon(Icons.favorite_border), findsOneWidget);
// Act: tap favorite
await tester.tap(find.byIcon(Icons.favorite_border));
await tester.pumpAndSettle();
// Assert: icon changed AND data persisted
expect(find.byIcon(Icons.favorite), findsOneWidget);
expect(fakePrefs.getStringList('favorites'), contains(testStop.id));
});
Bug this catches: UI updates but data doesn't persist (or vice versa).
Pattern 2: Navigation Integration Test
testWidgets('tapping map link navigates to map page', (tester) async {
await tester.pumpWidget(
ProviderScope(
child: MaterialApp.router(routerConfig: appRouter),
),
);
// Navigate to AI assistant
await tester.tap(find.text('AI 助手'));
await tester.pumpAndSettle();
// Simulate map link tap
// Find the MarkdownBody and trigger onTapLink
final markdownFinder = find.byType(MarkdownBody);
// ... trigger link callback
await tester.pumpAndSettle();
// Assert: now on map page
expect(find.byType(MapPage), findsOneWidget);
});
Pattern 3: Async Provider Integration
testWidgets('loading → data → display flow', (tester) async {
final fakeRepo = FakeVehicleRepository();
await tester.pumpWidget(
ProviderScope(
overrides: [
vehicleRepoProvider.overrideWithValue(fakeRepo),
],
child: MaterialApp(home: MapPage()),
),
);
// Initially shows loading
expect(find.byType(CircularProgressIndicator), findsOneWidget);
// Simulate data arrival
fakeRepo.emitVehicles(testVehicles);
await tester.pumpAndSettle();
// Shows data
expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.text('車號 ABC-123'), findsOneWidget);
});
Pattern 4: Error State Integration
testWidgets('API failure shows error UI with retry', (tester) async {
final fakeRepo = FakeVehicleRepository();
await tester.pumpWidget(testApp(fakeRepo));
// Simulate error
fakeRepo.emitError(Exception('Server 500'));
await tester.pumpAndSettle();
// Shows error message (not raw exception)
expect(find.text('Server 500'), findsNothing); // No raw error
expect(find.textContaining('暫時無法'), findsOneWidget);
// Retry button works
fakeRepo.willSucceedNext = true;
await tester.tap(find.text('重試'));
await tester.pumpAndSettle();
expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.textContaining('資料已更新'), findsOneWidget);
});
Pattern 5: E2E CRUD Flow
testWidgets('full CRUD: add → view → edit → delete favorite', (tester) async {
await tester.pumpWidget(testApp());
// CREATE
await tester.tap(find.byIcon(Icons.favorite_border).first);
await tester.pumpAndSettle();
expect(find.byIcon(Icons.favorite), findsOneWidget);
// READ - navigate to favorites
await tester.tap(find.text('設定'));
await tester.pumpAndSettle();
await tester.tap(find.text('我的收藏'));
await tester.pumpAndSettle();
expect(find.text(testStop.name), findsOneWidget);
// DELETE
await tester.tap(find.byIcon(Icons.delete).first);
await tester.pumpAndSettle();
expect(find.text(testStop.name), findsNothing);
});
Integration Test Structure
// test/e2e/feature_name_test.dart
import 'package:flutter_test/flutter_test.dart';
// Helper to create test app with all required overrides
Widget testApp({List? extraOverrides}) {
return ProviderScope(
overrides: [
// Always override external dependencies
sharedPreferencesProvider.overrideWithValue(FakeSharedPreferences()),
locationServiceProvider.overrideWithValue(FakeLocationService()),
...?extraOverrides,
],
child: MaterialApp.router(
routerConfig: appRouter,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
);
}
What to Test in Integration (Bug-Finding Priority)
- Cross-widget data flow — Provider state → Widget display
- Navigation with parameters — Route params arrive correctly
- User interaction sequences — Multi-step flows that exercise real paths
- Error recovery — Error state → retry → success
- State persistence — Data survives page navigation
- Responsive layout — Key UI works at different screen sizes
Anti-Patterns to AVOID
❌ Over-Mocking in Integration Tests
// BAD: Everything is mocked — you're testing nothing real
testWidgets('page loads', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
dataProvider.overrideWithValue(AsyncData(mockData)),
// 10 more overrides...
],
child: MockPage(), // Even the page is mocked!
),
);
expect(find.text('mock'), findsOneWidget); // Tests mock, not app
});
❌ No pumpAndSettle After State Change
// BAD: Assertions before widget tree rebuilds
await tester.tap(find.byIcon(Icons.add));
// Missing: await tester.pumpAndSettle();
expect(find.text('Added'), findsOneWidget); // FLAKY!
❌ Testing Widget Existence Without Behavior
// BAD: Only checks widget tree, not behavior
expect(find.byType(ElevatedButton), findsOneWidget);
// So what? Does the button DO anything?
// GOOD: Test the button works
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(someStateChanged, isTrue);
Localization in Tests
Always include localization delegates for widget tests that render localized text:
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: PageUnderTest(),
)
Verification Checklist
- [ ] Tests exercise real user flows (not just render checks)
- [ ] External deps (network, DB, GPS) are faked, not the components under test
- [ ] Error states tested (not just happy path)
- [ ] Navigation tested with parameter passing
- [ ] Async loading states verified (loading → data → error)
- [ ] No flaky time-dependent assertions
- [ ] All tests pass:
fvm flutter test
Related skills
integration-testing-dart— DISAMBIGUATION: integration-testing-dart covers multi-module Dart integration test patterns. flutter-integration-testing is Flutter-specific widget integration testing. Use flutter-integration-testing for Flutter widget tests; use integration-testing-dart for Dart module integration tests.test-driven-development— use TDD for integration test implementation.testing-anti-patterns— audit for mocking errors after integration tests pass.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ImL1s
- Source: ImL1s/flutter-claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.