Cursor rule
.cursor/rules/dart-testing.mdcCursor rules
Quality
85/100
Scores the file, not the repository.Length
595 words
13 headings · 8 code blocksRepository
6
— · pushed 80 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Dart/Flutter Testing89> This file extends [common/testing.md](../common/testing.md) with Dart and Flutter-specific content.1011## Test Framework1213- **flutter_test** / **dart:test** — built-in test runner14- **mockito** (with `@GenerateMocks`) or **mocktail** (no codegen) for mocking15- **bloc_test** for BLoC/Cubit unit tests16- **fake_async** for controlling time in unit tests17- **integration_test** for end-to-end device tests1819## Test Types2021| Type | Tool | Location | When to Write |22|------|------|----------|---------------|23| Unit | `dart:test` | `test/unit/` | All domain logic, state managers, repositories |24| Widget | `flutter_test` | `test/widget/` | All widgets with meaningful behavior |25| Golden | `flutter_test` | `test/golden/` | Design-critical UI components |26| Integration | `integration_test` | `integration_test/` | Critical user flows on real device/emulator |2728## Unit Tests: State Managers2930### BLoC with `bloc_test`3132```dart33group('CartBloc', () {34 late CartBloc bloc;35 late MockCartRepository repository;3637 setUp(() {38 repository = MockCartRepository();39 bloc = CartBloc(repository);40 });4142 tearDown(() => bloc.close());4344 blocTest<CartBloc, CartState>(45 'emits updated items when CartItemAdded',46 build: () => bloc,47 act: (b) => b.add(CartItemAdded(testItem)),48 expect: () => [CartState(items: [testItem])],49 );5051 blocTest<CartBloc, CartState>(52 'emits empty cart when CartCleared',53 seed: () => CartState(items: [testItem]),54 build: () => bloc,55 act: (b) => b.add(CartCleared()),56 expect: () => [const CartState()],57 );58});59```6061### Riverpod with `ProviderContainer`6263```dart64test('usersProvider loads users from repository', () async {65 final container = ProviderContainer(66 overrides: [userRepositoryProvider.overrideWithValue(FakeUserRepository())],67 );68 addTearDown(container.dispose);6970 final result = await container.read(usersProvider.future);71 expect(result, isNotEmpty);72});73```7475## Widget Tests7677```dart78testWidgets('CartPage shows item count badge', (tester) async {79 await tester.pumpWidget(80 ProviderScope(81 overrides: [82 cartNotifierProvider.overrideWith(() => FakeCartNotifier([testItem])),83 ],84 child: const MaterialApp(home: CartPage()),85 ),86 );8788 await tester.pump();89 expect(find.text('1'), findsOneWidget);90 expect(find.byType(CartItemTile), findsOneWidget);91});9293testWidgets('shows empty state when cart is empty', (tester) async {94 await tester.pumpWidget(95 ProviderScope(96 overrides: [cartNotifierProvider.overrideWith(() => FakeCartNotifier([]))],97 child: const MaterialApp(home: CartPage()),98 ),99 );100101 await tester.pump();102 expect(find.text('Your cart is empty'), findsOneWidget);103});104```105106## Fakes Over Mocks107108Prefer hand-written fakes for complex dependencies:109110```dart111class FakeUserRepository implements UserRepository {112 final _users = <String, User>{};113 Object? fetchError;114115 @override116 Future<User?> getById(String id) async {117 if (fetchError != null) throw fetchError!;118 return _users[id];119 }120121 @override122 Future<List<User>> getAll() async {123 if (fetchError != null) throw fetchError!;124 return _users.values.toList();125 }126127 @override128 Stream<List<User>> watchAll() => Stream.value(_users.values.toList());129130 @override131 Future<void> save(User user) async {132 _users[user.id] = user;133 }134135 @override136 Future<void> delete(String id) async {137 _users.remove(id);138 }139140 void addUser(User user) => _users[user.id] = user;141}142```143144## Async Testing145146```dart147// Use fake_async for controlling timers and Futures148test('debounce triggers after 300ms', () {149 fakeAsync((async) {150 final debouncer = Debouncer(delay: const Duration(milliseconds: 300));151 var callCount = 0;152 debouncer.run(() => callCount++);153 expect(callCount, 0);154 async.elapse(const Duration(milliseconds: 200));155 expect(callCount, 0);156 async.elapse(const Duration(milliseconds: 200));157 expect(callCount, 1);158 });159});160```161162## Golden Tests163164```dart165testWidgets('UserCard golden test', (tester) async {166 await tester.pumpWidget(167 MaterialApp(home: UserCard(user: testUser)),168 );169170 await expectLater(171 find.byType(UserCard),172 matchesGoldenFile('goldens/user_card.png'),173 );174});175```176177Run `flutter test --update-goldens` when intentional visual changes are made.178179## Test Naming180181Use descriptive, behavior-focused names:182183```dart184test('returns null when user does not exist', () { ... });185test('throws NotFoundException when id is empty string', () { ... });186testWidgets('disables submit button while form is invalid', (tester) async { ... });187```188189## Test Organization190191```192test/193├── unit/194│ ├── domain/195│ │ └── usecases/196│ └── data/197│ └── repositories/198├── widget/199│ └── presentation/200│ └── pages/201└── golden/202 └── widgets/203204integration_test/205└── flows/206 ├── login_flow_test.dart207 └── checkout_flow_test.dart208```209210## Coverage211212- Target 80%+ line coverage for business logic (domain + state managers)213- All state transitions must have tests: loading → success, loading → error, retry214- Run `flutter test --coverage` and inspect `lcov.info` with a coverage reporter215- Coverage failures should block CI when below threshold216
Also in ThanhTrunggDEV/DontBeLazy
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| ThanhTrunggDEV/DontBeLazy.cursor/rules/zh-agents.mdc · 6 | Cursor rules | no sections | 50/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/zh-patterns.mdc · 6 | Cursor rules | api | 30/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.agent/AGENTS.md · 6 | AGENTS.md | buildteststylearch+4 | 77/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/AGENTS.md · 6 | AGENTS.md | buildteststylearch+4 | 77/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-agents.mdc · 6 | Cursor rules | agent-behaviour | 50/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-code-review.mdc · 6 | Cursor rules | styletesting-strategygitsecurity+3 | 65/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-coding-style.mdc · 6 | Cursor rules | style | 54/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-development-workflow.mdc · 6 | Cursor rules | gitagent-behaviour | 39/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-git-workflow.mdc · 6 | Cursor rules | lint-formatgitagent-behaviour | 43/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-hooks.mdc · 6 | Cursor rules | styletypessecuritydo-not | 36/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-patterns.mdc · 6 | Cursor rules | lint-formatstyleapi | 52/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-performance.mdc · 6 | Cursor rules | buildperformance | 48/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-security.mdc · 6 | Cursor rules | security | 39/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-testing.mdc · 6 | Cursor rules | testtesting-strategyagent-behaviour | 34/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-coding-style.mdc · 6 | Cursor rules | lint-formatstyle | 52/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-hooks.mdc · 6 | Cursor rules | buildlint-formatdeployment | 60/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-patterns.mdc · 6 | Cursor rules | style | 54/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-security.mdc · 6 | Cursor rules | securityperformancedo-not | 73/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-testing.mdc · 6 | Cursor rules | testtesting-strategy | 55/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/csharp-coding-style.mdc · 6 | Cursor rules | lint-formatstyletypes | 66/100 | 3 days ago |
Diff against .cursor/rules/zh-agents.mdc Diff against .cursor/rules/zh-patterns.mdc Diff against .agent/AGENTS.md Diff against .cursor/AGENTS.md Diff against .cursor/rules/common-agents.mdc Diff against .cursor/rules/common-code-review.mdc Diff against .cursor/rules/common-coding-style.mdc Diff against .cursor/rules/common-development-workflow.mdc Diff against .cursor/rules/common-git-workflow.mdc Diff against .cursor/rules/common-hooks.mdc Diff against .cursor/rules/common-patterns.mdc Diff against .cursor/rules/common-performance.mdc Diff against .cursor/rules/common-security.mdc Diff against .cursor/rules/common-testing.mdc Diff against .cursor/rules/cpp-coding-style.mdc Diff against .cursor/rules/cpp-hooks.mdc Diff against .cursor/rules/cpp-patterns.mdc Diff against .cursor/rules/cpp-security.mdc Diff against .cursor/rules/cpp-testing.mdc Diff against .cursor/rules/csharp-coding-style.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
