1. Introduction
Flutter est un kit d'interface utilisateur Google qui permet de développer des applications esthétiques compilées de manière native pour les mobiles, le Web et les ordinateurs de bureau, à partir d'un seul codebase.
Dans cet atelier de programmation, vous allez développer et tester une application Flutter simple qui utilisera le package Provider pour gérer l'état.
Points abordés
- Créer des tests de widgets en utilisant le framework de tests de widgets
- Créer un test d'intégration pour évaluer l'interface utilisateur et les performances de l'application à l'aide de la bibliothèque
integration_test
- Tester des classes de données (fournisseurs) à l'aide de tests unitaires
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez commencer par développer une application simple contenant une liste d'éléments. Pour que vous puissiez passer directement aux tests, nous vous fournissons le code source. Cette application permet d'effectuer les opérations suivantes :
- Ajouter les éléments aux favoris
- Afficher la liste des favoris
- Supprimer des éléments de la liste des favoris
Une fois l'application terminée, vous allez écrire les tests suivants :
| GIF illustrant l'application exécutée sous Android |
Qu'attendez-vous de cet atelier de programmation ?
2. Configurer votre environnement de développement Flutter
Pour cet atelier, vous avez besoin de deux logiciels : le SDK Flutter et un éditeur.
Vous pouvez exécuter l'atelier de programmation sur l'un des appareils suivants :
- Un appareil Android ou iOS physique connecté à votre ordinateur et réglé en mode développeur
- Le simulateur iOS (les outils Xcode doivent être installés)
- L'émulateur Android (doit être configuré dans Android Studio)
- Un navigateur (Chrome est requis pour le débogage)
- En tant qu'application de bureau Windows, Linux ou macOS. Vous devez développer votre application sur la plate-forme où vous comptez la déployer. Par exemple, si vous voulez développer une application de bureau Windows, vous devez le faire sous Windows pour accéder à la chaîne de compilation appropriée. Les exigences spécifiques aux systèmes d'exploitation sont détaillées sur docs.flutter.dev/desktop.
3. Premiers pas
Créer une application Flutter et modifier les dépendances
Cet atelier de programmation consiste à tester une application mobile Flutter. Vous allez commencer par la créer rapidement à l'aide des fichiers sources que vous aurez copiés et collés. Le reste de l'atelier porte sur l'apprentissage des différents types de tests.
Créez une application Flutter simple à partir d'un modèle. Vous pouvez suivre les instructions dans Premiers pas avec votre première application Flutter ou celles des lignes de commande ci-dessous.
$ flutter create testing_app
Ajoutez des dépendances pub sur la ligne de commande. Pour une gestion facile de l'état, ajoutez provider
:
$ cd testing_app $ flutter pub add provider Resolving dependencies... collection 1.17.0 (1.17.1 available) js 0.6.5 (0.6.7 available) matcher 0.12.13 (0.12.14 available) meta 1.8.0 (1.9.0 available) + nested 1.0.0 path 1.8.2 (1.8.3 available) + provider 6.0.5 test_api 0.4.16 (0.4.18 available) Changed 2 dependencies!
Pour les tests autonomes du code Flutter sur vos appareils ou émulateurs ajoutez integration_test
:
$ flutter pub add --dev --sdk=flutter integration_test Resolving dependencies... + archive 3.3.2 (3.3.6 available) collection 1.17.0 (1.17.1 available) + crypto 3.0.2 + file 6.1.4 + flutter_driver 0.0.0 from sdk flutter + fuchsia_remote_debug_protocol 0.0.0 from sdk flutter + integration_test 0.0.0 from sdk flutter js 0.6.5 (0.6.7 available) matcher 0.12.13 (0.12.14 available) meta 1.8.0 (1.9.0 available) path 1.8.2 (1.8.3 available) + platform 3.1.0 + process 4.2.4 + sync_http 0.3.1 test_api 0.4.16 (0.4.18 available) + typed_data 1.3.1 + vm_service 9.4.0 (11.0.1 available) + webdriver 3.0.1 (3.0.2 available) Changed 12 dependencies!
Pour une API avancée permettant de tester les applications Flutter exécutées sur des appareils réels ou des émulateurs, ajoutez flutter_driver
:
$ flutter pub add --dev --sdk=flutter flutter_driver Resolving dependencies... archive 3.3.2 (3.3.6 available) collection 1.17.0 (1.17.1 available) js 0.6.5 (0.6.7 available) matcher 0.12.13 (0.12.14 available) meta 1.8.0 (1.9.0 available) path 1.8.2 (1.8.3 available) test_api 0.4.16 (0.4.18 available) vm_service 9.4.0 (11.0.1 available) webdriver 3.0.1 (3.0.2 available) Got dependencies!
Pour les outils de test généraux, ajoutez test
:
$ flutter pub add --dev test Resolving dependencies... + _fe_analyzer_shared 52.0.0 + analyzer 5.4.0 archive 3.3.2 (3.3.6 available) + args 2.3.2 collection 1.17.0 (1.17.1 available) + convert 3.1.1 + coverage 1.6.3 + frontend_server_client 3.2.0 + glob 2.1.1 + http_multi_server 3.2.1 + http_parser 4.0.2 + io 1.0.4 js 0.6.5 (0.6.7 available) + logging 1.1.1 matcher 0.12.13 (0.12.14 available) meta 1.8.0 (1.9.0 available) + mime 1.0.4 + node_preamble 2.0.1 + package_config 2.1.0 path 1.8.2 (1.8.3 available) + pool 1.5.1 + pub_semver 2.1.3 + shelf 1.4.0 + shelf_packages_handler 3.0.1 + shelf_static 1.1.1 + shelf_web_socket 1.0.3 + source_map_stack_trace 2.1.1 + source_maps 0.10.11 + test 1.22.0 (1.23.0 available) test_api 0.4.16 (0.4.18 available) + test_core 0.4.20 (0.4.23 available) vm_service 9.4.0 (11.0.1 available) + watcher 1.0.2 + web_socket_channel 2.3.0 webdriver 3.0.1 (3.0.2 available) + webkit_inspection_protocol 1.2.0 + yaml 3.1.1 Changed 28 dependencies!
Pour prendre en charge la navigation dans les applications, ajoutez go_router :
$ flutter pub add go_router Resolving dependencies... archive 3.3.2 (3.3.6 available) collection 1.17.0 (1.17.1 available) + flutter_web_plugins 0.0.0 from sdk flutter + go_router 6.0.4 js 0.6.5 (0.6.7 available) matcher 0.12.13 (0.12.14 available) meta 1.8.0 (1.9.0 available) path 1.8.2 (1.8.3 available) test 1.22.0 (1.23.0 available) test_api 0.4.16 (0.4.18 available) test_core 0.4.20 (0.4.23 available) vm_service 9.4.0 (11.0.1 available) webdriver 3.0.1 (3.0.2 available) Changed 2 dependencies!
Les dépendance suivantes devraient avoir été ajoutées à pubspec.yaml :
Sous dependencies
:
dependencies: provider: ^6.0.5 go_router: ^6.0.4
Sous dev_dependencies
:
dev_dependencies: integration_test: sdk: flutter flutter_driver: sdk: flutter test: ^1.22.0
Ouvrez le projet dans l'éditeur de code de votre choix, puis exécutez l'application. Vous pouvez aussi l'exécuter à l'aide de la ligne de commande suivante.
$ flutter run
4. Développer l'application
Vous allez maintenant créer l'application pour pouvoir la tester. Elle contient les fichiers suivants :
lib/models/favorites.dart
(crée la classe de modèle pour la liste des favoris)lib/screens/favorites.dart
(crée la disposition de la liste des favoris)lib/screens/home.dart
(crée une liste d'éléments)lib/main.dart
(fichier principal dans lequel l'application démarre)
Créer le modèle Favorites
dans le fichier lib/models/favorites.dart
Créez un sous-répertoire models
dans le répertoire lib
, puis créez un fichier nommé favorites.dart
. Dans ce fichier , ajoutez le code suivant :
lib/models/favorites.dart
import 'package:flutter/material.dart';
/// The [Favorites] class holds a list of favorite items saved by the user.
class Favorites extends ChangeNotifier {
final List<int> _favoriteItems = [];
List<int> get items => _favoriteItems;
void add(int itemNo) {
_favoriteItems.add(itemNo);
notifyListeners();
}
void remove(int itemNo) {
_favoriteItems.remove(itemNo);
notifyListeners();
}
}
Ajouter la page des favoris dans le fichier lib/screens/favorites.dart
Dans le répertoire lib
, créez un sous-répertoire screens
puis créez un fichier nommé favorites.dart
. Dans ce fichier , ajoutez le code suivant :
lib/screens/favorites.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/favorites.dart';
class FavoritesPage extends StatelessWidget {
const FavoritesPage({super.key});
static String routeName = 'favorites_page';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Favorites'),
),
body: Consumer<Favorites>(
builder: (context, value, child) => ListView.builder(
itemCount: value.items.length,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => FavoriteItemTile(value.items[index]),
),
),
);
}
}
class FavoriteItemTile extends StatelessWidget {
const FavoriteItemTile(this.itemNo, {super.key});
final int itemNo;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('favorites_text_$itemNo'),
),
trailing: IconButton(
key: Key('remove_icon_$itemNo'),
icon: const Icon(Icons.close),
onPressed: () {
Provider.of<Favorites>(context, listen: false).remove(itemNo);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Removed from favorites.'),
duration: Duration(seconds: 1),
),
);
},
),
),
);
}
}
Ajouter la page d'accueil dans le fichier lib/screens/home.dart
Dans le répertoire lib/screens
, créez un autre fichier que vous nommerez home.dart
. Dans lib/screens/home.dart
, ajoutez le code suivant :
lib/screens/home.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../models/favorites.dart';
import 'favorites.dart';
class HomePage extends StatelessWidget {
static String routeName = '/';
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Testing Sample'),
actions: <Widget>[
TextButton.icon(
onPressed: () {
context.go('/${FavoritesPage.routeName}');
},
icon: const Icon(Icons.favorite_border),
label: const Text('Favorites'),
),
],
),
body: ListView.builder(
itemCount: 100,
cacheExtent: 20.0,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => ItemTile(index),
),
);
}
}
class ItemTile extends StatelessWidget {
final int itemNo;
const ItemTile(this.itemNo, {super.key});
@override
Widget build(BuildContext context) {
var favoritesList = Provider.of<Favorites>(context);
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('text_$itemNo'),
),
trailing: IconButton(
key: Key('icon_$itemNo'),
icon: favoritesList.items.contains(itemNo)
? const Icon(Icons.favorite)
: const Icon(Icons.favorite_border),
onPressed: () {
!favoritesList.items.contains(itemNo)
? favoritesList.add(itemNo)
: favoritesList.remove(itemNo);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(favoritesList.items.contains(itemNo)
? 'Added to favorites.'
: 'Removed from favorites.'),
duration: const Duration(seconds: 1),
),
);
},
),
),
);
}
}
Remplacer le contenu du fichier lib/main.dart
Remplacez le contenu du fichier lib/main.dart
par le code suivant :
lib/main.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'models/favorites.dart';
import 'screens/favorites.dart';
import 'screens/home.dart';
void main() {
runApp(const TestingApp());
}
final _router = GoRouter(
routes: [
GoRoute(
path: HomePage.routeName,
builder: (context, state) {
return const HomePage();
},
routes: [
GoRoute(
path: FavoritesPage.routeName,
builder: (context, state) {
return const FavoritesPage();
},
),
],
),
],
);
class TestingApp extends StatelessWidget {
const TestingApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Favorites>(
create: (context) => Favorites(),
child: MaterialApp.router(
title: 'Testing Sample',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
routerConfig: _router,
),
);
}
}
L'application est maintenant terminée, mais n'a pas encore été testée.
Exécutez l'application. Elle doit ressembler à la capture d'écran ci-dessous :
L'application affiche une liste d'éléments. Pour ajouter un élément à vos favoris, appuyez sur l'icône correspondante en forme de cœur (de sorte que ce cœur soit rempli). Le bouton Favoris de la AppBar
vous redirige vers un second écran qui contient la liste des favoris.
L'application est maintenant prête à être testée. Vous commencerez les tests à la prochaine étape.
5. Test unitaire du fournisseur
Vous commencerez par des tests unitaires du modèle favorites
. Qu'est-ce qu'un test unitaire ? Un test unitaire permet de vérifier que chaque unité individuelle de logiciel (fonction, objet ou widget) exécute correctement la tâche prévue.
Tous les fichiers de test d'une application Flutter (hormis pour les tests d'intégration) sont placés dans le répertoire test
.
Supprimer test/widget_test.dart
Avant de commencer les tests, supprimez le fichier widget_test.dart
. Vous allez ajouter vos propres fichiers de test.
Créer un fichier de test
Tout d'abord, vous allez tester la méthode add()
dans le modèle Favorites
pour vérifier qu'un nouvel élément est ajouté à la liste, et que celle-ci reflète bien cette modification. Par convention, la structure de répertoires dans le répertoire test
reprend celle du répertoire lib
. Les fichiers Dart portent le même nom, mais avec le suffixe _test
.
Dans le répertoire test
, créez d'abord un sous-répertoire models
, puis dans celui-ci, un fichier favorites_test.dart
contenant ce qui suit :
test/models/favorites_test.dart
import 'package:test/test.dart';
import 'package:testing_app/models/favorites.dart';
void main() {
group('Testing App Provider', () {
var favorites = Favorites();
test('A new item should be added', () {
var number = 35;
favorites.add(number);
expect(favorites.items.contains(number), true);
});
});
}
Le framework de test Flutter vous permet d'associer des tests similaires liés les uns aux autres dans un groupe. Il peut y avoir plusieurs groupes dans un seul fichier de test destinés à tester différentes parties du fichier correspondant dans le répertoire lib
.
La méthode test()
utilise deux paramètres de position : la description
du test et le callback
dans lequel vous écrivez en fait le test.
Testez la suppression d'un élément de la liste. Insérez le test suivant dans le même groupe Testing App Provider
:
test/models/favorites_test.dart
test('An item should be removed', () {
var number = 45;
favorites.add(number);
expect(favorites.items.contains(number), true);
favorites.remove(number);
expect(favorites.items.contains(number), false);
});
Exécuter le test
Dans la ligne de commande, accédez au répertoire racine du projet et saisissez la commande suivante :
$ flutter test test/models/favorites_test.dart
Si tout fonctionne correctement, vous devriez voir un message de ce type :
00:06 +2: All tests passed!
Fichier de test complet : test/models/favorites_test.dart
.
Pour en savoir plus sur les tests unitaires, lisez l'introduction aux tests unitaires.
6. Test de widgets
Au cours de cette étape, vous allez ajouter du code pour tester les widgets. Les tests de widgets sont propres à Flutter, qui permet de tester chaque widget de façon isolée. Cette étape permet de tester les écrans HomePage
et FavoritesPage
individuellement.
Les tests de widgets font appel à la fonction testWidget()
au lieu de la fonction test()
. Comme la fonction test()
, la fonction testWidget()
accepte deux paramètres : description,
et callback
. Ce dernier accepte WidgetTester
comme argument.
Les tests de widgets utilisent TestFlutterWidgetsBinding
, une classe qui fournit les mêmes ressources à vos widgets que dans une application en cours d'exécution (par exemple, des informations sur la taille de l'écran, la possibilité de planifier des animations, etc.), mais sans exécuter l'application elle-même. Au lieu de cela, un environnement virtuel est utilisé pour instancier le widget, puis tester les résultats. Ici, pumpWidget
lance le processus en demandant au framework d'installer et de mesurer un widget particulier comme il le ferait dans une application.
Le framework de test de widgets fournit des outils de recherche pour trouver les widgets (par exemple, text()
, byType()
, byIcon().
) et des outils de mise en correspondance pour vérifier les résultats.
Commencez par tester le widget HomePage
.
Créer un fichier de test
Le premier test consiste à vérifier si la HomePage
défile correctement.
Dans le répertoire test
, créez un fichier que vous nommerez home_test.dart
et dans lequel vous ajouterez le code suivant :
test/home_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/home.dart';
Widget createHomeScreen() => ChangeNotifierProvider<Favorites>(
create: (context) => Favorites(),
child: const MaterialApp(
home: HomePage(),
),
);
void main() {
group('Home Page Widget Tests', () {
testWidgets('Testing Scrolling', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.text('Item 0'), findsOneWidget);
await tester.fling(
find.byType(ListView),
const Offset(0, -200),
3000,
);
await tester.pumpAndSettle();
expect(find.text('Item 0'), findsNothing);
});
});
}
La fonction createHomeScreen()
permet de créer une application qui charge le widget à tester dans un MaterialApp, encapsulé dans un ChangeNotifierProvider. Dans l'arborescence des widgets, le widget HomePage doit toujours figurer au-dessous de ces deux widgets pour hériter de ceux-ci et accéder aux données qu'ils proposent. Cette fonction est transmise en tant que paramètre à la fonction pumpWidget()
.
Vérifiez ensuite si le framework peut trouver une ListView
affichée à l'écran.
Ajoutez l'extrait de code suivant à home_test.dart
:
test/home_test.dart
group('Home Page Widget Tests', () {
// BEGINNING OF NEW CONTENT
testWidgets('Testing if ListView shows up', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.byType(ListView), findsOneWidget);
});
// END OF NEW CONTENT
testWidgets('Testing Scrolling', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.text('Item 0'), findsOneWidget);
await tester.fling(
find.byType(ListView),
const Offset(0, -200),
3000,
);
await tester.pumpAndSettle();
expect(find.text('Item 0'), findsNothing);
});
});
Exécuter le test
Exécutez d'abord le test comme vous le feriez pour un test unitaire, à l'aide de la commande suivante :
$ flutter test test/home_test.dart
Ce test devrait être rapide et produire un message semblable à ce qui suit :
00:02 +2: All tests passed!
Vous pouvez également effectuer les tests de widgets avec un appareil ou un émulateur, ce qui permet d'observer l'exécution du test et de redémarrer à chaud.
Branchez votre appareil ou démarrez l'émulateur. Vous pouvez aussi exécuter le test en tant qu'application de bureau.
À partir de la ligne de commande, accédez au répertoire racine du projet et saisissez la commande suivante :
$ flutter run test/home_test.dart
Vous devrez peut-être sélectionner l'appareil sur lequel exécuter le test. Dans ce cas, suivez les instructions et choisissez un appareil :
Multiple devices found:
Linux (desktop) • linux • linux-x64 • Ubuntu 22.04.1 LTS 5.15.0-58-generic
Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119
[1]: Linux (linux)
[2]: Chrome (chrome)
Please choose one (To quit, press "q/Q"):
Si tout fonctionne, vous devriez obtenir un résultat semblable à celui-ci :
Launching test/home_test.dart on Linux in debug mode...
Building Linux application...
flutter: 00:00 +0: Home Page Widget Tests Testing if ListView shows up
Syncing files to device Linux... 62ms
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
💪 Running with sound null safety 💪
An Observatory debugger and profiler on Linux is available at: http://127.0.0.1:35583/GCpdLBqf2UI=/
flutter: 00:00 +1: Home Page Widget Tests Testing Scrolling
The Flutter DevTools debugger and profiler on Linux is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:35583/GCpdLBqf2UI=/
flutter: 00:02 +2: All tests passed!
Vous allez ensuite modifier le fichier de test et appuyer sur Shift + R
pour redémarrer l'application et exécuter à nouveau tous les tests. N'interrompez pas l'application.
Ajoutez d'autres tests au groupe qui teste les widgets Homepage. Copiez le test suivant dans votre fichier :
test/home_test.dart
testWidgets('Testing IconButtons', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.byIcon(Icons.favorite), findsNothing);
await tester.tap(find.byIcon(Icons.favorite_border).first);
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Added to favorites.'), findsOneWidget);
expect(find.byIcon(Icons.favorite), findsWidgets);
await tester.tap(find.byIcon(Icons.favorite).first);
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Removed from favorites.'), findsOneWidget);
expect(find.byIcon(Icons.favorite), findsNothing);
});
Ce test permet de vérifier que lorsque l'utilisateur appuie sur l'IconButton
, le cœur vide (Icons.favorite_border
) devient plein (Icons.favorite
), puis redevient vide (Icons.favorite_border
) si l'utilisateur appuie de nouveau dessus.
Appuyez sur Shift + R
. L'application redémarre à chaud et tous les tests sont à nouveau exécutés.
Fichier de test complet : test/home_test.dart
.
Suivez la même procédure pour tester la FavoritesPage
avec le code suivant, puis exécutez le test.
test/favorites_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';
late Favorites favoritesList;
Widget createFavoritesScreen() => ChangeNotifierProvider<Favorites>(
create: (context) {
favoritesList = Favorites();
return favoritesList;
},
child: const MaterialApp(
home: FavoritesPage(),
),
);
void addItems() {
for (var i = 0; i < 10; i += 2) {
favoritesList.add(i);
}
}
void main() {
group('Favorites Page Widget Tests', () {
testWidgets('Test if ListView shows up', (tester) async {
await tester.pumpWidget(createFavoritesScreen());
addItems();
await tester.pumpAndSettle();
expect(find.byType(ListView), findsOneWidget);
});
testWidgets('Testing Remove Button', (tester) async {
await tester.pumpWidget(createFavoritesScreen());
addItems();
await tester.pumpAndSettle();
var totalItems = tester.widgetList(find.byIcon(Icons.close)).length;
await tester.tap(find.byIcon(Icons.close).first);
await tester.pumpAndSettle();
expect(tester.widgetList(find.byIcon(Icons.close)).length,
lessThan(totalItems));
expect(find.text('Removed from favorites.'), findsOneWidget);
});
});
}
Ce test permet de vérifier si un élément disparaît lorsqu'un utilisateur appuie sur le bouton de fermeture (supprimer).
Pour en savoir plus sur les tests de widgets, consultez les ressources ci-dessous :
7. Tester l'interface utilisateur de l'application via des tests d'intégration
Les tests d'intégration permettent de tester la façon dont des éléments individuels d'une application fonctionnent ensemble globalement. Le package integration_test
est utilisé pour effectuer des tests d'intégration dans Flutter. Il s'agit de la version Flutter de Selenium WebDriver, Protractor, Espresso, ou Earl Gray. Le package utilise flutter_driver
en interne pour effectuer le test sur un appareil.
L'écriture de tests d'intégration dans Flutter ressemble à l'écriture des tests de widget, à la différence que les tests d'intégration sont exécutés sur un "appareil cible", qui peut être un appareil mobile, un navigateur ou une application de bureau.
Écrire le test
Dans le répertoire racine du projet, créez d'abord un sous-répertoire nommé integration_test
. Ensuite, créez un fichier app_test.dart
dans ce sous-répertoire.
integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:testing_app/main.dart';
void main() {
group('Testing App', () {
testWidgets('Favorites operations test', (tester) async {
await tester.pumpWidget(const TestingApp());
final iconKeys = [
'icon_0',
'icon_1',
'icon_2',
];
for (var icon in iconKeys) {
await tester.tap(find.byKey(ValueKey(icon)));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Added to favorites.'), findsOneWidget);
}
await tester.tap(find.text('Favorites'));
await tester.pumpAndSettle();
final removeIconKeys = [
'remove_icon_0',
'remove_icon_1',
'remove_icon_2',
];
for (final iconKey in removeIconKeys) {
await tester.tap(find.byKey(ValueKey(iconKey)));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Removed from favorites.'), findsOneWidget);
}
});
});
}
Exécuter le test
Branchez votre appareil ou démarrez l'émulateur. Vous pouvez aussi exécuter le test en tant qu'application de bureau.
Dans la ligne de commande, accédez au répertoire racine du projet et saisissez la commande suivante :
$ flutter test integration_test/app_test.dart
Si tout fonctionne, vous devriez obtenir un résultat semblable à celui-ci :
Multiple devices found:
Linux (desktop) • linux • linux-x64 • Ubuntu 22.04.1 LTS 5.15.0-58-generic
Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119
[1]: Linux (linux)
[2]: Chrome (chrome)
Please choose one (To quit, press "q/Q"): 1
00:00 +0: loading /home/miquel/tmp/testing_app/integration_test/app_test.dart B00:08 +0: loading /home/miquel/tmp/testing_app/integration_test/app_test.dart
00:26 +1: All tests passed!
8. Tester les performances de l'application avec Flutter Driver
Écrire un test de performances
Dans le répertoire "integration_test", créez un nouveau fichier de test nommé perf_test.dart avec le contenu suivant :
integration_test/perf_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:testing_app/main.dart';
void main() {
group('Testing App Performance', () {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
testWidgets('Scrolling test', (tester) async {
await tester.pumpWidget(const TestingApp());
final listFinder = find.byType(ListView);
await binding.traceAction(() async {
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
await tester.fling(listFinder, const Offset(0, 500), 10000);
await tester.pumpAndSettle();
}, reportKey: 'scrolling_summary');
});
});
}
La fonction ensureInitialized()
vérifie si le pilote d'intégration est initialisé et le réinitialise si nécessaire. Il est utile de définir framePolicy
sur fullyLive
pour tester du code animé.
Ce test fait d'abord défiler la liste des éléments très rapidement, puis toute la page vers le haut. La fonction traceAction()
enregistre les actions et génère un résumé de la chronologie.
Enregistrer les performances
Pour consigner les résultats, créez un répertoire nommé test_driver
puis, dans ce répertoire, créez un fichier nommé perf_driver.dart
et ajoutez-y le code suivant :
test_driver/perf_driver.dart
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() {
return integrationDriver(
responseDataCallback: (data) async {
if (data != null) {
final timeline = driver.Timeline.fromJson(
data['scrolling_summary'] as Map<String, dynamic>);
final summary = driver.TimelineSummary.summarize(timeline);
await summary.writeTimelineToFile(
'scrolling_summary',
pretty: true,
includeSummary: true,
);
}
},
);
}
Exécuter le test
Branchez votre appareil ou démarrez l'émulateur.
Dans la ligne de commande, accédez au répertoire racine du projet et saisissez la commande suivante :
$ flutter drive \
--driver=test_driver/perf_driver.dart \
--target=integration_test/perf_test.dart \
--profile \
--no-dds
Si tout fonctionne, vous devriez obtenir un résultat semblable à celui-ci :
Running "flutter pub get" in testing_app...
Resolving dependencies...
archive 3.3.2 (3.3.6 available)
collection 1.17.0 (1.17.1 available)
js 0.6.5 (0.6.7 available)
matcher 0.12.13 (0.12.14 available)
meta 1.8.0 (1.9.0 available)
path 1.8.2 (1.8.3 available)
test 1.22.0 (1.23.0 available)
test_api 0.4.16 (0.4.18 available)
test_core 0.4.20 (0.4.23 available)
vm_service 9.4.0 (11.0.1 available)
webdriver 3.0.1 (3.0.2 available)
Got dependencies!
Running Gradle task 'assembleProfile'... 1,379ms
✓ Built build/app/outputs/flutter-apk/app-profile.apk (14.9MB).
Installing build/app/outputs/flutter-apk/app-profile.apk... 222ms
I/flutter ( 6125): 00:04 +1: Testing App Performance (tearDownAll)
I/flutter ( 6125): 00:04 +2: All tests passed!
All tests passed.
Une fois le test réussi, vous trouverez deux fichiers dans le répertoire de build à la racine du projet :
scrolling_summary.timeline_summary.json
contient le résumé. Ouvrez-le dans n'importe quel éditeur de texte pour le consulter.scrolling_summary.timeline.json
contient les données complètes de la chronologie.
Pour en savoir plus sur les tests d'intégration, consultez ces ressources :
9. Félicitations !
Vous avez terminé cet atelier de programmation et appris plusieurs manières de tester une application Flutter.
Connaissances acquises
- Tester les fournisseurs via des tests unitaires
- Tester des widgets en utilisant le framework de test de widgets
- Tester l'interface utilisateur de l'application via des tests d'intégration
- Tester les performances d'une application via des tests d'intégration
Pour en savoir plus sur les tests dans Flutter, consultez les pages suivantes :