agentsclimarketplace

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

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill flutter-testing

Assembled 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.

Keep looking

Skills are one crate of 326,758. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.