Przejdź do treści
Biblioteka skilli
Programowanie · Testowanie

flutter-testing

/flutter-testing

Pomaga pisać i recenzować testy aplikacji Flutter z Riverpod, repozytoriami opartymi na Result i SDK za kontraktami: testy unit, widget z override providerów, golden oraz wybór między mockiem a fake.

Licencja
MIT
Ostatnia zmiana repozytorium
02.10.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
4
Skrypty
0
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 02.10.2026, bez odwołań do wycofanych modeli i API.

Licencja

MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.

Co robi i kiedy

Pomaga pisać i recenzować testy aplikacji Flutter z Riverpod, repozytoriami opartymi na Result i SDK za kontraktami: testy unit, widget z override providerów, golden oraz wybór między mockiem a fake.

Oryginalny opis (EN)

Use when writing or reviewing tests for a Flutter app that uses Riverpod, Result-based repositories and SDKs behind contracts - unit tests for data sources, repositories and notifiers, widget tests with provider overrides, listener tests with recording fakes, golden tests, or deciding between a mock and a fake.

Jak działa

  1. Layout
  2. Mock or fake
  3. Helpers (copy from assets/)
  4. Recipes
  5. Principles
  6. Review checklist
Sekcje z nagłówków SKILL.md, w oryginale · 191 linii, 895 słów
Podgląd SKILL.md
---
name: flutter-testing
description: Use when writing or reviewing tests for a Flutter app that uses Riverpod, Result-based repositories and SDKs behind contracts - unit tests for data sources, repositories and notifiers, widget tests with provider overrides, listener tests with recording fakes, golden tests, or deciding between a mock and a fake.
---

# Testing a contract-based Flutter app

The architecture makes testing cheap: every boundary is a contract you can
override. Test **behaviour at the boundaries**, not implementation details.

## Layout

```text
test/
├── helpers/            test_container.dart, pump_app.dart, fakes, helpers.dart (barrel)
├── core/               mirrors lib/core
├── features/<feature>/ mirrors lib/features/<feature>; goldens/ beside the test
└── shared/
```

One test file per source file, named `<file>_test.dart`. Group by class.

## Mock or fake

| Use | When |
|---|---|
| **Recording fake** (a hand-written class that implements the contract and stores what it received) | Stateful app-owned contracts: `AnalyticsService`, `ErrorTrackingService`, `NotificationService`. Assertions read as "this event was tracked". |
| **`mocktail` mock** | A repository or data-source contract with canned answers: `when(repo.getDashboard).thenAnswer((_) async => Success(dashboard))`. |
| **Fake `ApiClient`** | Testing a remote data source: throw an `ApiException`, assert the `Result`. Never mock Dio here. |
| **Mock `Dio`** | Only in the `DioApiClient` test itself. |

If you find yourself mocking an SDK inside a feature test, the SDK is not
behind a contract (see `flutter-sdk-contracts`).

## Helpers (copy from `assets/`)

- `test_container.dart`: `createContainer(overrides:)` disposes itself and turns
  **retry off**. Riverpod 3 retries failing providers with backoff by default,
  which makes failure assertions slow and flaky.
- `pump_app.dart`: `tester.pumpApp(widget, overrides:)` mounts under the real
  theme and localizations and returns the container.
- `recording_analytics_service.dart`: from the `flutter-sdk-contracts` assets.

## Recipes

**1. Data source: failure mapping**

```dart
test('maps 401 to AuthFailure', () async {
  final api = _FakeApiClient(throws: const ApiResponseException(statusCode: 401));
  final result = await ProfileRemoteDataSourceImpl(api).fetchProfile();
  expect(result.exceptionOrNull(), isA<AuthFailure>());
});
```

Cover: success, 401/403 → `AuthFailure`, 422 → `ValidationFailure`, 5xx →
`ServerFailure`, connection error → `NetworkFailure`, timeout → `NetworkFailure`,
anything else → `UnknownFailure`.

**2. Repository: model → entity**

Fake the data source, return a model, assert the entity. Also assert a failure
is passed through **unchanged**.

**3. Notifier**

```dart
test('loads items from the repository', () async {
  final repo = _MockItemRepository();
  when(repo.getPage).thenAnswer((_) async => Success(page));
  final container = createContainer(
    overrides: [itemRepositoryProvider.overrideWithValue(repo)],
  );

  final state = await container.read(itemsProvider.future);

  expect(state.items, page.items);
});
```

- Override the **repository contract**; `data/` is never touched.
- For `autoDispose` providers, hold a listener (`keepAlive` helper) or the
  provider can be disposed between reads.
- Replace a dependency notifier with a test subclass:
  `learningProvider.overrideWith(_RecordingLearningNotifier.new)`.

**4. Submission notifier: dispose mid-flight**

```dart
test('survives disposal while the action is in flight', () async {
  final completer = Completer<Result<String>>();
  final subscription = container.listen(provider, (_, _) {});
  final pending = container.read(provider.notifier).submit(completer.future);

  subscription.close();           // the screen goes away
  await container.pump();
  completer.complete(const Success('late'));

  await expectLater(pending, completion(isA<Success<String, AppFailure>>()));
});
```

Without the `ref.mounted` guard this throws after disposal.

**5. Widget**

```dart
testWidgets('shows an error with a retry', (tester) async {
  await tester.pumpApp(
    const ItemsPage(),
    overrides: [itemRepositoryProvider.overrideWithValue(_FailingRepository())],
  );
  await tester.pumpAndSettle();
  expect(find.text('Try again'), findsOneWidget);
});
```

**6. Listener widget**

Mount only the listener over an empty child with `UncontrolledProviderScope`,
override the contracts (recording analytics, mock repository), drive the state
and assert:

```dart
final analytics = RecordingAnalyticsService();
final container = createContainer(overrides: [
  analyticsServiceProvider.overrideWithValue(analytics),
]);
await tester.pumpWidget(UncontrolledProviderScope(
  container: container,
  child: const ItemsListeners(child: SizedBox()),
));

container.read(itemSubmissionProvider.notifier).add();
await tester.pump();

expect(analytics.events.single, isA<ItemChangedEvent>());
```

End by unmounting (`await tester.pumpWidget(const SizedBox())`) before the
container is disposed.

**7. `DioApiClient`**

Mock `Dio` and assert exception mapping: timeouts and connection errors →
`ApiConnectionException`, bad responses → `ApiResponseException` with status
and `code`, anything else → `ApiUnknownException`. Stub the same method the
adapter calls (`dio.request<Map<String, dynamic>>(...)` in the reference
adapter).

**8. Goldens** — `assets/golden_test_template.dart`

- Tag them (`@Tags(['golden'])`) so they can be run or skipped separately.
- Load one known font; fix the surface size and `devicePixelRatio`.
- One golden per visual **state**, and per shipped **locale** (copy length changes layout).
- Generate and compare on **one OS** in CI; font rendering differs across platforms.
- Update deliberately: `flutter test --tags golden --update-goldens`.

**9. Pin framework behaviour you rely on**

When a feature depends on a library behaviour that is not yours to guarantee
(for example, a failed refetch keeps the previous value so pull-to-refresh can
report the error without blanking the list), write a small test that asserts it:

```dart
expect(state, isA<AsyncError<String>>());
expect(state.hasValue, isTrue);   // failed AND still holds the old value
```

A package upgrade that drops it then fails a test instead of showing up as
vanishing content on a bad connection, far from any code you changed.

## Principles

- **Arrange with overrides, not with real wiring.** If a test needs `Dio` or
  Firebase, a contract is missing.
- **Assert outcomes**: state, events recorded, entities returned. Avoid
  `verify(...).called(n)` unless the call count *is* the behaviour.
- **Regression first.** A bug fix starts with a failing test that names the bug
  in a comment: why it failed, not only what was asserted.
- **No `sleep`.** Use `tester.pump(duration)`, `fake_async`, or `container.pump()`.
- **Dispose** every `ProviderContainer` (`addTearDown`).
- Verify layer rules separately with `check_boundaries.sh` from
  `flutter-feature-scaffold`; tests are not the place for import hygiene.

## Review checklist

- [ ] Overrides at the contract boundary; no SDK or Dio mocked in feature tests
- [ ] Recording fakes for stateful services, mocks for canned repository answers
- [ ] Retry disabled in test containers; containers disposed
- [ ] Every failure branch of a data source has a test
- [ ] Notifier tests cover load success, load failure, and a mutation failure that keeps state
- [ ] Goldens tagged, one per state and locale, generated on one OS
- [ ] A regression test accompanies each bug fix
Pliki w paczce (4)
  • SKILL.md 7 KB
  • assets/golden_test_template.dart 3 KB
  • assets/pump_app.dart 1 KB
  • assets/test_container.dart 1 KB

Wersje

RepozytoriumWynikLicencja
Ta strona · ethiel97/flutter-clean-riverpod-skills100MIT
HoangNguyen0403/agent-skills-standard100MIT
testland/qa100MIT
thiennc-tesoglobal/flutter-skills100BSD-3-Clause
jyotiraditya-chauhan/test-kit100MIT
madteacher/mad-agents-skills100MIT
dhruvanbhalara/skills90MIT

Migawka bazy z 28.10.2026 · wersja paczki d628d5ebfd9e (skrót SHA-256 całej paczki)

Pobrania w ostatnich 30 dniach: 0 · ranking

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 100testing
    Pomaga pisać i recenzować testy we Flutter i Dart: jednostkowe, widżetów i golden, naprawiać niestabilne testy, dodawać pokrycie i wybierać między testami jednostkowymi a widżetów.
  • 100flutter-tester
    Pomaga tworzyć, naprawiać i recenzować testy w projekcie Flutter: unit, widget, integration, testowanie providerów Riverpod i mocki Mockito, ze wzorcami Given-When-Then i setupem GetIt, SharedPreferences i FakeDatabase.
  • 95testing-flutter
    Pomaga pisać testy i strategię testowania aplikacji Flutter 3.47 z BLoC v9 i Riverpod 3.
  • 85flutter-core:flutter-testing-quality
    Pisze testy aplikacji Flutter: jednostkowe, widgetów, integracyjne i golden, z mockami, pokryciem i profilowaniem w DevTools.
  • 100flutter-test-strategy
    Pomaga zdecydować, co testować w kodzie Flutter i Dart na której warstwie (unit, widget, golden, integration) oraz ustawić progi pokrycia.
  • 100riverpod-patterns
    Podpowiada wzorce Riverpod dla aplikacji Flutter: pisanie providerów, obsługę AsyncValue, użycie ref i cykl życia providerów.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.