Flutter-Tests

In diesem Abschnitt werden drei Arten von Tests in Flutter vorgestellt: Unit-Tests, Widget-Tests und Integrationstests.


Überblick über Testtypen

TypeBeschreibungBefehle ausführen
Unit-TestingTesten einzelner Funktionen und Klassenmethodenflutter test
Widget-TestsTesten der Benutzeroberfläche eines einzelnen Widgetsflutter test
IntegrationstestsTesten des vollständigen Anwendungsablaufsflutter test integration_test/

Unit-Testing

Unit-Tests werden verwendet, um Geschäftslogik-Code zu testen.

Beispiel: Unit-Test-Beispiel

// math_utils.dart - zu testender Code
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(Division durch Null nicht möglich);
    return a / b;
  }

  static int factorial(int n) {
    if (n < 0) throw ArgumentError(Negative Zahlen haben keine Fakultät);
    if (n == 0 || n == 1) return 1;
    return n * factorial(n - 1);
  }
}

// math_utils_test.dart - Testdatei
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/math_utils.dart';

void main() {
  group('MathUtils', () {
    test(add addiert zwei Zahlen, () {
      expect(MathUtils.add(2, 3), equals(5));
    });

    test(subtract subtrahiert zwei Zahlen, () {
      expect(MathUtils.subtract(5, 3), equals(2));
    });

    test(multiply multipliziert zwei Zahlen, () {
      expect(MathUtils.multiply(4, 5), equals(20));
    });

    test(divide dividiert zwei Zahlen, () {
      expect(MathUtils.divide(10, 2), equals(5));
    });

    test(divide wirft eine Ausnahme beim Teilen durch Null, () {
      expect(() => MathUtils.divide(10, 0), throwsArgumentError);
    });

    test(factorial berechnet die Fakultät, () {
      expect(MathUtils.factorial(5), equals(120));
      expect(MathUtils.factorial(0), equals(1));
    });

    test(factorial wirft eine Ausnahme für negative Zahlen, () {
      expect(() => MathUtils.factorial(-1), throwsArgumentError);
    });
  });
}

Widget-Tests

Widget-Tests werden zum Testen von UI-Komponenten verwendet.

Beispiel: Widget-Test

// counter_widget.dart - zu testendes Widget
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(Anzahl: $_count, key: const Key('counter_text')),
        ElevatedButton(
          key: const Key('increment_button'),
          onPressed: _increment,
          child: const Text('Erhöhen'),
        ),
      ],
    );
  }
}

// counter_widget_test.dart - Testdatei
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter_widget.dart';

void main() {
  testWidgets(CounterWidget zeigt anfänglichen Zählerstand an, (WidgetTester tester) async {
    // Widget aufbauen
    await tester.pumpWidget(const MaterialApp(
      home: CounterWidget(),
    ));

    // Anfängliche Zählerstandsanzeige überprüfen
    expect(find.text(Count: 0), findsOneWidget);
  });

  testWidgets(CounterWidget erhöht den Zählerstand beim Klick auf die Schaltfläche, (WidgetTester tester) async {
    await tester.pumpWidget(const MaterialApp(
      home: CounterWidget(),
    ));

    // Schaltfläche finden und anklicken
    await tester.tap(find.byKey(const Key('increment_button')));
    await tester.pump();

    // Überprüfen, ob die Anzahl aktualisiert wurde
    expect(find.text(Anzahl: 1), findsOneWidget);

    // Erneut klicken
    await tester.tap(find.byKey(const Key('increment_button')));
    await tester.pump();

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

Integrationstests

Integrationstests werden verwendet, um den vollständigen Funktionsablauf der Anwendung zu testen.

Beispiel: Integrationstest

// 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-Integrationstest, () {
    testWidgets(Vollständiger Benutzerfluss-Test, (WidgetTester tester) async {
      // App starten
      await tester.pumpWidget(const MyApp());
      await tester.pumpAndSettle();

      // Überprüfen, ob die Startseite angezeigt wird
      expect(find.text('Startseite'), findsOneWidget);

      // Klicken, um zur Detailseite zu navigieren
      await tester.tap(find.text('Details'));
      await tester.pumpAndSettle();

      // Anzeige der Detailseite überprüfen
      expect(find.text('Detailseite'), findsOneWidget);

      // Zurück klicken
      await tester.tap(find.byIcon(Icons.arrow_back));
      await tester.pumpAndSettle();

      // Rückkehr zur Startseite überprüfen
      expect(find.text('Startseite'), findsOneWidget);
    });
  });
}

Eine gute Testabdeckung kann die Codequalität verbessern; es wird empfohlen, Unit-Tests zumindest für die zentrale Geschäftslogik zu schreiben.

Weitere Erweiterungen