Flutter testing
Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/flutter-testing
When to activate: Flutter testing, widget tests, unit tests, integration tests, mockito, golden tests, pump, pumpWidget, WidgetTesterFrom its SKILL.md
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill flutter-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.1 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it
Flutter Testing Patterns
Unit Tests
// test/unit/cart_test.dart
import 'package:flutter_test/flutter_test.dart';
void main() {
group('Cart', () {
late Cart cart;
setUp(() => cart = Cart());
test('starts empty', () {
expect(cart.items, isEmpty);
expect(cart.total, equals(0.0));
});
test('adds item and updates total', () {
cart.add(Item(name: 'Widget', price: 9.99));
expect(cart.items.length, 1);
expect(cart.total, closeTo(9.99, 0.01));
});
test('throws on negative price', () {
expect(() => cart.add(Item(name: 'X', price: -1)), throwsArgumentError);
});
});
}
Mockito / Mocktail
// Using mockito (with build_runner code gen)
@GenerateMocks([UserRepository])
import 'user_repository_test.mocks.dart';
void main() {
late MockUserRepository mockRepo;
late UserService sut;
setUp(() {
mockRepo = MockUserRepository();
sut = UserService(mockRepo);
});
test('returns user from repo', () async {
when(mockRepo.fetchUser('1')).thenAnswer((_) async => User(id: '1', name: 'Alice'));
final user = await sut.getUser('1');
expect(user.name, 'Alice');
verify(mockRepo.fetchUser('1')).called(1);
});
test('throws on repo failure', () {
when(mockRepo.fetchUser(any)).thenThrow(Exception('Network error'));
expect(() => sut.getUser('1'), throwsException);
});
}
Widget Tests
void main() {
testWidgets('LoginForm shows error on empty submit', (tester) async {
await tester.pumpWidget(const MaterialApp(home: LoginForm()));
// Interact
await tester.tap(find.byType(ElevatedButton));
await tester.pump(); // trigger rebuild
// Assert
expect(find.text('Email is required'), findsOneWidget);
});
testWidgets('Counter increments on tap', (tester) async {
await tester.pumpWidget(const MaterialApp(home: CounterPage()));
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('1'), findsOneWidget);
});
testWidgets('shows loading then data', (tester) async {
await tester.pumpWidget(MaterialApp(home: UserPage(userId: '1')));
// Loading state
expect(find.byType(CircularProgressIndicator), findsOneWidget);
// Resolve futures
await tester.pumpAndSettle();
// Data state
expect(find.text('Alice'), findsOneWidget);
});
}
Finders
find.text('Submit') // by exact text
find.textContaining('Submit') // by substring
find.byType(ElevatedButton) // by widget type
find.byKey(const Key('submit')) // by key
find.byIcon(Icons.send) // by icon
find.ancestor(of: find.text('X'), matching: find.byType(Card)) // ancestor
find.descendant(of: find.byType(Card), matching: find.text('X')) // descendant
find.byWidgetPredicate((w) => w is Text && w.data!.startsWith('A')) // predicate
pump vs pumpAndSettle
await tester.pump(); // single frame — for setState
await tester.pump(Duration(seconds: 1)); // advance clock by duration
await tester.pumpAndSettle(); // frames until no more scheduled — for animations/futures
Golden Tests
void main() {
testWidgets('UserCard golden', (tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(body: UserCard(user: fakeUser)),
));
await expectLater(
find.byType(UserCard),
matchesGoldenFile('goldens/user_card.png'),
);
});
}
// Run: flutter test --update-goldens to regenerate
Integration Tests
// integration_test/app_test.dart
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('full login flow', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(find.byKey(const Key('email')), '[email protected]');
await tester.enterText(find.byKey(const Key('password')), 'secret');
await tester.tap(find.byKey(const Key('login-btn')));
await tester.pumpAndSettle();
expect(find.text('Welcome'), findsOneWidget);
});
}
Testing Riverpod Providers
void main() {
test('userProvider returns user', () async {
final container = ProviderContainer(overrides: [
userRepositoryProvider.overrideWithValue(FakeUserRepository()),
]);
addTearDown(container.dispose);
final user = await container.read(userProvider('1').future);
expect(user.name, 'Test User');
});
}
Test Structure Conventions
test/
├── unit/
│ ├── models/
│ └── services/
├── widget/
│ ├── components/
│ └── pages/
├── goldens/
│ └── *.png
integration_test/
└── app_test.dart
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.