Flutter-Tests
In diesem Abschnitt werden drei Arten von Tests in Flutter vorgestellt: Unit-Tests, Widget-Tests und Integrationstests.
Überblick über Testtypen
| Type | Beschreibung | Befehle ausführen |
|---|---|---|
| Unit-Testing | Testen einzelner Funktionen und Klassenmethoden | flutter test |
| Widget-Tests | Testen der Benutzeroberfläche eines einzelnen Widgets | flutter test |
| Integrationstests | Testen des vollständigen Anwendungsablaufs | flutter 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);
});
});
}
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);
});
}
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);
});
});
}
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);
});
});
}
Weitere ErweiterungenEine gute Testabdeckung kann die Codequalität verbessern; es wird empfohlen, Unit-Tests zumindest für die zentrale Geschäftslogik zu schreiben.