chore(legacy): add test infrastructure + mocktail
This commit is contained in:
@@ -74,6 +74,7 @@ dev_dependencies:
|
|||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
|
mocktail: ^1.0.4
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
23
modules/legacy/modules/account/test/canary_test.dart
Normal file
23
modules/legacy/modules/account/test/canary_test.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:sf_shared/testing.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets(
|
||||||
|
'pumpApp renders child inside ProviderScope + MaterialApp',
|
||||||
|
(tester) async {
|
||||||
|
await pumpApp(
|
||||||
|
tester,
|
||||||
|
child: const Text('canary'),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('canary'), findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test('makeContainer creates a disposable ProviderContainer', () {
|
||||||
|
final container = makeContainer();
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
expect(container, isNotNull);
|
||||||
|
});
|
||||||
|
}
|
||||||
6
packages/sf_shared/lib/src/testing/container_helper.dart
Normal file
6
packages/sf_shared/lib/src/testing/container_helper.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_riverpod/misc.dart' show Override;
|
||||||
|
|
||||||
|
ProviderContainer makeContainer({List<Override> overrides = const []}) {
|
||||||
|
return ProviderContainer(overrides: overrides);
|
||||||
|
}
|
||||||
35
packages/sf_shared/lib/src/testing/pump_app.dart
Normal file
35
packages/sf_shared/lib/src/testing/pump_app.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_riverpod/misc.dart' show Override;
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:sf_localizations/sf_localizations.dart';
|
||||||
|
|
||||||
|
/// Set [withLocalizations] only when the widget uses `context.translate(...)`;
|
||||||
|
/// it requires the JSON asset bundle loaded in the test environment.
|
||||||
|
Future<void> pumpApp(
|
||||||
|
WidgetTester tester, {
|
||||||
|
required Widget child,
|
||||||
|
List<Override> overrides = const [],
|
||||||
|
Locale locale = const Locale('es'),
|
||||||
|
bool withLocalizations = false,
|
||||||
|
}) async {
|
||||||
|
final delegates = <LocalizationsDelegate<Object>>[
|
||||||
|
if (withLocalizations) SFLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
];
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: overrides,
|
||||||
|
child: MaterialApp(
|
||||||
|
locale: locale,
|
||||||
|
supportedLocales: supportedLanguages.map(Locale.new).toList(),
|
||||||
|
localizationsDelegates: delegates,
|
||||||
|
home: Scaffold(body: child),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
2
packages/sf_shared/lib/testing.dart
Normal file
2
packages/sf_shared/lib/testing.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export 'src/testing/container_helper.dart';
|
||||||
|
export 'src/testing/pump_app.dart';
|
||||||
@@ -12,6 +12,10 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
sf_infrastructure:
|
sf_infrastructure:
|
||||||
path: ../../packages/sf_infrastructure
|
path: ../../packages/sf_infrastructure
|
||||||
@@ -41,12 +45,11 @@ dependencies:
|
|||||||
permission_handler: ^12.0.1
|
permission_handler: ^12.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
riverpod_generator: ^3.0.3
|
riverpod_generator: ^3.0.3
|
||||||
build_runner: ^2.7.1
|
build_runner: ^2.7.1
|
||||||
riverpod_lint: ^3.0.3
|
riverpod_lint: ^3.0.3
|
||||||
|
mocktail: ^1.0.4
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|||||||
@@ -1196,6 +1196,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.0"
|
version: "5.5.0"
|
||||||
|
mocktail:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mocktail
|
||||||
|
sha256: "5e1bf53cc7baa8062a33b84424deb61513858ea05c601b8509e683815b5914aa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
mustache_template:
|
mustache_template:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
121
test/README.md
Normal file
121
test/README.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Tests — sf-app-platform
|
||||||
|
|
||||||
|
Patrón estándar para los tests del monorepo. Cada package/module tiene su propia carpeta `test/`.
|
||||||
|
|
||||||
|
## Setup mínimo
|
||||||
|
|
||||||
|
Todo package que vaya a tener tests debe agregar:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# pubspec.yaml
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
mocktail: ^1.0.4
|
||||||
|
```
|
||||||
|
|
||||||
|
Y si va a consumir los helpers compartidos:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies: # puede ir en dev_dependencies si solo se usa en tests
|
||||||
|
sf_shared:
|
||||||
|
path: ../../packages/sf_shared # ajustar ruta relativa
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helpers compartidos — `package:sf_shared/testing.dart`
|
||||||
|
|
||||||
|
Se importan SOLO desde archivos `test/**`:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:sf_shared/testing.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pumpApp(tester, child:, overrides:, locale:)`
|
||||||
|
|
||||||
|
Pone el widget bajo test dentro de `ProviderScope` + `MaterialApp` + `SFLocalizations`.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
testWidgets('my screen shows title', (tester) async {
|
||||||
|
await pumpApp(
|
||||||
|
tester,
|
||||||
|
child: const MyScreen(),
|
||||||
|
overrides: [
|
||||||
|
myRepositoryProvider.overrideWithValue(MockRepository()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('Title'), findsOneWidget);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### `makeContainer(overrides:)`
|
||||||
|
|
||||||
|
Para unit-testing de notifiers/providers sin widgets:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
test('controller emits AsyncError on failure', () async {
|
||||||
|
final container = makeContainer(
|
||||||
|
overrides: [
|
||||||
|
myRepositoryProvider.overrideWithValue(ThrowingRepository()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
|
||||||
|
await container.read(myControllerProvider.notifier).submit();
|
||||||
|
|
||||||
|
expect(container.read(myControllerProvider).hasError, isTrue);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mockeo de repositorios con mocktail
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class MockUsersRepository extends Mock implements UsersRepository {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MockUsersRepository mockRepo;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockRepo = MockUsersRepository();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('loads users happy path', () async {
|
||||||
|
when(() => mockRepo.getUsers(userId: any(named: 'userId')))
|
||||||
|
.thenAnswer((_) async => [const UserEntity(...)]);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Correr tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Todos los tests del package actual
|
||||||
|
flutter test
|
||||||
|
|
||||||
|
# Un archivo específico
|
||||||
|
flutter test test/canary_test.dart
|
||||||
|
|
||||||
|
# Con coverage
|
||||||
|
flutter test --coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
Para correr tests de un package del monorepo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd modules/legacy/modules/account && flutter test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Convenciones
|
||||||
|
|
||||||
|
- **1 archivo de test por archivo de producción** cuando sea posible (`foo_screen.dart` → `foo_screen_test.dart`).
|
||||||
|
- **Happy path + ≥1 error path** por feature migrada (ver `sf-legacy-migration-checklist.md`).
|
||||||
|
- **Golden tests** — no hay setup global todavía. Si una feature lo justifica, discutir antes.
|
||||||
|
- **Mocks no autogenerados** — usamos mocktail con `Mock implements XRepository` en lugar de build_runner para mocks.
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- [mocktail docs](https://pub.dev/packages/mocktail)
|
||||||
|
- [flutter_test docs](https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html)
|
||||||
|
- `sf-legacy-migration-checklist.md` — plan de migración que consume estos helpers.
|
||||||
Reference in New Issue
Block a user