Tests Flutter

Cette section présentera trois types de tests dans Flutter : les tests unitaires, les tests de widgets et les tests d'intégration.


Aperçu des types de tests

TypeDescriptionExécuter des commandes
Tests unitairesTester des fonctions individuelles et des méthodes de classeflutter test
Tests de widgetsTester l'interface utilisateur d'un seul Widgetflutter test
Tests d’intégrationTester le flux complet de l'applicationflutter test integration_test/

Tests unitaires

Les tests unitaires servent à tester le code de la logique métier.

Exemple : Exemple de test unitaire

// math_utils.dart - code à tester
class MathUtils {
  static int add(int a, int b) => a + b;
  static int subtract(int a, int b) => a - b;
  static int multiply(int a, int b) => a * b;
  static double divide(int a, int b) {
    if (b == 0) throw ArgumentError(Impossible de diviser par zéro);
    return a / b;
  }

  static int factorial(int n) {
    if (n < 0) throw ArgumentError(Les nombres négatifs n'ont pas de factorielle);
    if (n == 0 || n == 1) return 1;
    return n * factorial(n - 1);
  }
}

// math_utils_test.dart - fichier de test
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/math_utils.dart';

void main() {
  group('MathUtils', () {
    test(add additionne deux nombres, () {
      expect(MathUtils.add(2, 3), equals(5));
    });

    test(subtract soustrait deux nombres, () {
      expect(MathUtils.subtract(5, 3), equals(2));
    });

    test(multiply multiplie deux nombres, () {
      expect(MathUtils.multiply(4, 5), equals(20));
    });

    test(divide divise deux nombres, () {
      expect(MathUtils.divide(10, 2), equals(5));
    });

    test(divide lève une exception lors d'une division par zéro, () {
      expect(() => MathUtils.divide(10, 0), throwsArgumentError);
    });

    test(factorial calcule la factorielle, () {
      expect(MathUtils.factorial(5), equals(120));
      expect(MathUtils.factorial(0), equals(1));
    });

    test(factorial lève une exception pour les nombres négatifs, () {
      expect(() => MathUtils.factorial(-1), throwsArgumentError);
    });
  });
}

Tests de widgets

Les tests de widgets sont utilisés pour tester les composants d'interface utilisateur.

Exemple : test de widget

// counter_widget.dart - Widget en cours de test
class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(Nombre : $_count, key: const Key('counter_text')),
        ElevatedButton(
          key: const Key('increment_button'),
          onPressed: _increment,
          child: const Text('Increase'),
        ),
      ],
    );
  }
}

// counter_widget_test.dart - fichier de test
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter_widget.dart';

void main() {
  testWidgets(CounterWidget affiche le nombre initial, (WidgetTester tester) async {
    // Construire le widget
    await tester.pumpWidget(const MaterialApp(
      home: CounterWidget(),
    ));

    // Vérifier l'affichage du nombre initial
    expect(find.text(Count: 0), findsOneWidget);
  });

  testWidgets(CounterWidget incrémente le nombre lors du clic sur le bouton, (WidgetTester tester) async {
    await tester.pumpWidget(const MaterialApp(
      home: CounterWidget(),
    ));

    // Trouver et cliquer sur le bouton
    await tester.tap(find.byKey(const Key('increment_button')));
    await tester.pump();

    // Vérifier que le compteur a été mis à jour
    expect(find.text(Count: 1), findsOneWidget);

    // Cliquer à nouveau
    await tester.tap(find.byKey(const Key('increment_button')));
    await tester.pump();

    expect(find.text(Count: 2), findsOneWidget);
  });
}

Tests d’intégration

Les tests d'intégration servent à tester le flux complet des fonctionnalités de l'application.

Exemple : test d'intégration

// integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group(App integration test, () {
    testWidgets(Test complet du parcours utilisateur, (WidgetTester tester) async {
      // Lancer l'application
      await tester.pumpWidget(const MyApp());
      await tester.pumpAndSettle();

      // Vérifier que la page d'accueil est affichée
      expect(find.text('Accueil'), findsOneWidget);

      // Cliquer pour naviguer vers la page de détail
      await tester.tap(find.text('Détails'));
      await tester.pumpAndSettle();

      // Vérifier l'affichage de la page de détail
      expect(find.text('Page de détail'), findsOneWidget);

      // Cliquer sur retour
      await tester.tap(find.byIcon(Icons.arrow_back));
      await tester.pumpAndSettle();

      // Vérifier le retour à l'accueil
      expect(find.text('Accueil'), findsOneWidget);
    });
  });
}

Une bonne couverture de tests peut améliorer la qualité du code ; il est recommandé d'écrire des tests unitaires pour au moins la logique métier principale.

autres extensions