From 63ead4508286590fa5d4c483b6f27c19dbc84256 Mon Sep 17 00:00:00 2001
From: Adrien Bonnin <adbonnin@ippon.fr>
Date: Sun, 18 Sep 2022 20:04:10 +0200
Subject: [PATCH] :sparkles: Ajout des tests pour le overrides

---
 .gitignore                                    |   1 +
 .gitlab-ci.yml                                |   2 +
 README.md                                     |  16 +-
 .../06_state_notifier_provider.dart           |  19 --
 lib/toto.md                                   | 177 -----------
 pubspec.lock                                  | 280 ++++++++++++++++++
 pubspec.yaml                                  |   2 +
 test/presentation/07_overrides_test.dart      |  41 +++
 8 files changed, 341 insertions(+), 197 deletions(-)
 delete mode 100644 lib/toto.md
 create mode 100644 test/presentation/07_overrides_test.dart

diff --git a/.gitignore b/.gitignore
index 74075fe..1d1cba0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ migrate_working_dir/
 .pub-cache/
 .pub/
 /build/
+*.mocks.dart
 
 # Web related
 lib/generated_plugin_registrant.dart
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5152928..bbaab2e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,6 +7,8 @@ test:flutter:
   stage: test
   image: cirrusci/flutter
   script:
+    - flutter pub get
+    - flutter pub run build_runner build --delete-conflicting-outputs
     - flutter test
 
 build:web-main:
diff --git a/README.md b/README.md
index ce1ef8a..c0cd3b9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,18 @@
 # Riverpod par la pratique
 
 * [Découverte des bases](docs/01_decouverte_des_bases.md)
-* [Intégration avec Flutter](docs/02_integration_avec_flutter.md)
\ No newline at end of file
+* [Intégration avec Flutter](docs/02_integration_avec_flutter.md)
+
+## Environnement de développement
+
+Récupérer les dépendances :
+
+```shell script
+$ flutter pub get
+```
+
+Lancer le générateur de code :
+
+```shell script
+$ flutter pub run build_runner build --delete-conflicting-outputs
+```
\ No newline at end of file
diff --git a/lib/presentation/06_state_notifier_provider.dart b/lib/presentation/06_state_notifier_provider.dart
index d3300d9..35815df 100644
--- a/lib/presentation/06_state_notifier_provider.dart
+++ b/lib/presentation/06_state_notifier_provider.dart
@@ -130,17 +130,6 @@ class TodosChangeNotifier extends ChangeNotifier {
   }
 }
 
-class MutableTodosChangeNotifier extends ChangeNotifier {
-  MutableTodosChangeNotifier(this.todos);
-
-  final List<Todo> todos;
-
-  void updateTodo(Todo updatedTodo) {
-    todos.updateTodo(updatedTodo);
-    notifyListeners();
-  }
-}
-
 const todos = [
   Todo(
     id: 0,
@@ -200,12 +189,4 @@ extension TodoListExtension on List<Todo> {
 
   List<Todo> copyWithTodo(Todo updatedTodo) => //
       [for (Todo todo in this) todo.id == updatedTodo.id ? updatedTodo : todo];
-
-  void updateTodo(Todo updatedTodo) {
-    for (int i = 0; i < length; i++) {
-      if (this[i].id == updatedTodo.id) {
-        this[i] = updatedTodo;
-      }
-    }
-  }
 }
diff --git a/lib/toto.md b/lib/toto.md
deleted file mode 100644
index 7c0e3fd..0000000
--- a/lib/toto.md
+++ /dev/null
@@ -1,177 +0,0 @@
-```dart
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-
-class TodoExample extends StatelessWidget {
-  const TodoExample({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return const ProviderScope(
-      child: TodoList(),
-    );
-  }
-}
-
-class TodoList extends ConsumerStatefulWidget {
-  const TodoList({Key? key}) : super(key: key);
-
-  @override
-  ConsumerState<TodoList> createState() => _TodoListState();
-}
-
-class _TodoListState extends ConsumerState<TodoList> {
-  bool _isUncheckedFilter = false;
-
-  @override
-  Widget build(BuildContext context) {
-    final todos = ref.watch(todosProvider.select((tds) => tds.whereTodos(_isUncheckedFilter)));
-
-    return Column(
-      children: [
-        TodoListFilter(
-          filter: _isUncheckedFilter,
-          onFilterChanged: _onFilterChanged,
-        ),
-        const Divider(height: 1),
-        Expanded(
-          child: ListView.builder(
-            itemBuilder: (_, index) =>
-                TodoListItem(
-                  todos[index],
-                  onCheckedChanged: (value) => _onCheckedChanged(todos[index], value),
-                ),
-            itemCount: todos.length,
-          ),
-        ),
-      ],
-    );
-  }
-
-  void _onFilterChanged(bool value) {
-    setState(() => _isUncheckedFilter = value);
-  }
-
-  void _onCheckedChanged(Todo todo, bool? value) {
-    final updatedTodo = todo.copyWith(checked: value != null && value);
-
-    final todos = ref.read(todosProvider.notifier).state;
-    ref.read(todosProvider.notifier).state = todos.copyWithTodo(updatedTodo);
-  }
-}
-
-class TodoListFilter extends StatelessWidget {
-  const TodoListFilter({
-    Key? key,
-    required this.filter,
-    required this.onFilterChanged,
-  }) : super(key: key);
-
-  final bool filter;
-  final ValueChanged<bool> onFilterChanged;
-
-  @override
-  Widget build(BuildContext context) {
-    return Padding(
-      padding: const EdgeInsets.symmetric(horizontal: 16),
-      child: Row(
-        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-        children: [
-          const Text("Unchecked"),
-          Switch(
-            value: filter,
-            onChanged: onFilterChanged,
-          ),
-        ],
-      ),
-    );
-  }
-}
-
-class TodoListItem extends StatelessWidget {
-  const TodoListItem(this.todo, {
-    Key? key,
-    this.onCheckedChanged,
-  }) : super(key: key);
-
-  final Todo todo;
-  final ValueChanged<bool?>? onCheckedChanged;
-
-  @override
-  Widget build(BuildContext context) {
-    return CheckboxListTile(
-      value: todo.checked,
-      onChanged: onCheckedChanged,
-      title: Text(
-        todo.title,
-        overflow: TextOverflow.ellipsis,
-      ),
-      controlAffinity: ListTileControlAffinity.leading,
-    );
-  }
-}
-
-final todosProvider = StateProvider<List<Todo>>((ref) => todos);
-
-const todos = [
-  Todo(
-    id: 0,
-    title: 'Unit test passed',
-    checked: true,
-  ),
-  Todo(
-    id: 1,
-    title: 'Code reviewed',
-    checked: true,
-  ),
-  Todo(
-    id: 2,
-    title: 'Acceptance criteria for each issue met',
-  ),
-  Todo(
-    id: 3,
-    title: 'Functional tests passed',
-  ),
-  Todo(
-    id: 4,
-    title: 'Non-functional requirements met',
-  ),
-  Todo(
-    id: 5,
-    title: 'Product owner accepts the User Story',
-  ),
-];
-
-class Todo {
-  const Todo({
-    required this.id,
-    required this.title,
-    this.checked = false,
-  });
-
-  final int id;
-  final String title;
-  final bool checked;
-
-  Todo copyWith({
-    int? id,
-    String? title,
-    bool? checked,
-  }) {
-    return Todo(
-      id: id ?? this.id,
-      title: title ?? this.title,
-      checked: checked ?? this.checked,
-    );
-  }
-}
-
-extension TodoListExtension on List<Todo> {
-  List<Todo> whereTodos(bool isUnchecked) => //
-  where((todo) => !isUnchecked || !todo.checked).toList();
-
-  List<Todo> copyWithTodo(Todo updatedTodo) => //
-  [for (Todo todo in this) todo.id == updatedTodo.id ? updatedTodo : todo];
-}
-
-```
\ No newline at end of file
diff --git a/pubspec.lock b/pubspec.lock
index dbdf552..18dd95a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,27 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
+  _fe_analyzer_shared:
+    dependency: transitive
+    description:
+      name: _fe_analyzer_shared
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "47.0.0"
+  analyzer:
+    dependency: transitive
+    description:
+      name: analyzer
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.7.0"
+  args:
+    dependency: transitive
+    description:
+      name: args
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.3.1"
   async:
     dependency: transitive
     description:
@@ -15,6 +36,62 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.0"
+  build:
+    dependency: transitive
+    description:
+      name: build
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.3.1"
+  build_config:
+    dependency: transitive
+    description:
+      name: build_config
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.1.0"
+  build_daemon:
+    dependency: transitive
+    description:
+      name: build_daemon
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.1.0"
+  build_resolvers:
+    dependency: transitive
+    description:
+      name: build_resolvers
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.10"
+  build_runner:
+    dependency: "direct dev"
+    description:
+      name: build_runner
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.2.1"
+  build_runner_core:
+    dependency: transitive
+    description:
+      name: build_runner_core
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "7.2.4"
+  built_collection:
+    dependency: transitive
+    description:
+      name: built_collection
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "5.1.1"
+  built_value:
+    dependency: transitive
+    description:
+      name: built_value
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "8.4.1"
   characters:
     dependency: transitive
     description:
@@ -29,6 +106,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.3.1"
+  checked_yaml:
+    dependency: transitive
+    description:
+      name: checked_yaml
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.1"
   clock:
     dependency: transitive
     description:
@@ -36,6 +120,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.1.0"
+  code_builder:
+    dependency: transitive
+    description:
+      name: code_builder
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.3.0"
   collection:
     dependency: transitive
     description:
@@ -43,6 +134,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.16.0"
+  convert:
+    dependency: transitive
+    description:
+      name: convert
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.0.2"
+  crypto:
+    dependency: transitive
+    description:
+      name: crypto
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.0.2"
   cupertino_icons:
     dependency: "direct main"
     description:
@@ -50,6 +155,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.5"
+  dart_style:
+    dependency: transitive
+    description:
+      name: dart_style
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.2.4"
   fake_async:
     dependency: transitive
     description:
@@ -57,6 +169,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.3.0"
+  file:
+    dependency: transitive
+    description:
+      name: file
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "6.1.4"
+  fixnum:
+    dependency: transitive
+    description:
+      name: fixnum
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -86,6 +212,20 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  frontend_server_client:
+    dependency: transitive
+    description:
+      name: frontend_server_client
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.3"
+  glob:
+    dependency: transitive
+    description:
+      name: glob
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.0"
   go_router:
     dependency: "direct main"
     description:
@@ -93,6 +233,34 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "4.5.0"
+  graphs:
+    dependency: transitive
+    description:
+      name: graphs
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.0"
+  http_multi_server:
+    dependency: transitive
+    description:
+      name: http_multi_server
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.2.1"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.0.1"
+  io:
+    dependency: transitive
+    description:
+      name: io
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.3"
   js:
     dependency: transitive
     description:
@@ -100,6 +268,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.6.4"
+  json_annotation:
+    dependency: transitive
+    description:
+      name: json_annotation
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.6.0"
   lints:
     dependency: transitive
     description:
@@ -135,6 +310,27 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.7.0"
+  mime:
+    dependency: transitive
+    description:
+      name: mime
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.2"
+  mockito:
+    dependency: "direct dev"
+    description:
+      name: mockito
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "5.3.1"
+  package_config:
+    dependency: transitive
+    description:
+      name: package_config
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.0"
   path:
     dependency: transitive
     description:
@@ -142,6 +338,27 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.8.1"
+  pool:
+    dependency: transitive
+    description:
+      name: pool
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.5.1"
+  pub_semver:
+    dependency: transitive
+    description:
+      name: pub_semver
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.1"
+  pubspec_parse:
+    dependency: transitive
+    description:
+      name: pubspec_parse
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.2.1"
   riverpod:
     dependency: transitive
     description:
@@ -149,11 +366,32 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.0-dev.9"
+  shelf:
+    dependency: transitive
+    description:
+      name: shelf
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.2"
+  shelf_web_socket:
+    dependency: transitive
+    description:
+      name: shelf_web_socket
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.2"
   sky_engine:
     dependency: transitive
     description: flutter
     source: sdk
     version: "0.0.99"
+  source_gen:
+    dependency: transitive
+    description:
+      name: source_gen
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.2.3"
   source_span:
     dependency: transitive
     description:
@@ -182,6 +420,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.0"
+  stream_transform:
+    dependency: transitive
+    description:
+      name: stream_transform
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.0"
   string_scanner:
     dependency: transitive
     description:
@@ -203,6 +448,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.4.9"
+  timing:
+    dependency: transitive
+    description:
+      name: timing
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.1"
   vector_math:
     dependency: transitive
     description:
@@ -210,6 +469,27 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.2"
+  watcher:
+    dependency: transitive
+    description:
+      name: watcher
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
+  web_socket_channel:
+    dependency: transitive
+    description:
+      name: web_socket_channel
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.2.0"
+  yaml:
+    dependency: transitive
+    description:
+      name: yaml
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.1.1"
 sdks:
   dart: ">=2.17.1 <3.0.0"
   flutter: ">=3.0.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 3cfd5b0..f48e120 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -47,6 +47,8 @@ dev_dependencies:
   # package. See that file for information about deactivating specific lint
   # rules and activating additional ones.
   flutter_lints: ^2.0.0
+  mockito: ^5.1.0
+  build_runner: ^2.1.10
 
 # For information on the generic Dart part of this file, see the
 # following page: https://dart.dev/tools/pub/pubspec
diff --git a/test/presentation/07_overrides_test.dart b/test/presentation/07_overrides_test.dart
new file mode 100644
index 0000000..3b6dd90
--- /dev/null
+++ b/test/presentation/07_overrides_test.dart
@@ -0,0 +1,41 @@
+import 'package:article_flutter_riverpod/presentation/07_overrides_example.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import '07_overrides_test.mocks.dart';
+
+@GenerateMocks([TodosRepository])
+void main() {
+  group('TodoExample', () {
+    testWidgets('doit vérifier', (tester) async {
+      // given:
+      const mocksTodos = [
+        Todo(id: 0, title: 'Test'),
+      ];
+
+      // and:
+      final mockTodosRepository = MockTodosRepository();
+      when(mockTodosRepository.getTodos()).thenReturn(mocksTodos);
+
+      // when:
+      await tester.pumpWidget(
+        ProviderScope(
+          overrides: [
+            todosRepositoryProvider.overrideWithValue(mockTodosRepository),
+          ],
+          child: const MaterialApp(
+            home: Material(
+              child: TodoList(),
+            ),
+          ),
+        ),
+      );
+
+      // then:
+      expect(find.byType(TodoListItem), findsOneWidget);
+    });
+  });
+}
-- 
GitLab