Testing Flutter Apps
Prerequisites: Testing with package:test, Navigation and app architecture
Flutter supports three kinds of tests, from fast and narrow to slow and broad:
| Kind | Tests | Speed |
|---|---|---|
| Unit tests | A function, class, or ViewModel | Very fast |
| Widget tests | One widget or screen, without a device | Fast |
| Integration tests | The whole app on a device or emulator | Slow |
Most of your tests should be unit and widget tests.
Unit testing a ViewModel with a fake
import 'package:flutter_test/flutter_test.dart';
class FakeArticleRepository implements ArticleRepository {
FakeArticleRepository({this.fail = false});
final bool fail;
@override
Future<List<Article>> latest() async {
if (fail) throw Exception('offline');
return const [Article(1, 'Hello')];
}
}
void main() {
test('loads articles', () async {
final vm = ArticlesViewModel(FakeArticleRepository());
await vm.load();
expect(vm.articles, hasLength(1));
expect(vm.error, isNull);
});
test('shows an error when loading fails', () async {
final vm = ArticlesViewModel(FakeArticleRepository(fail: true));
await vm.load();
expect(vm.error, isNotNull);
});
}
Widget tests
A widget test builds a widget in a test environment and interacts with it:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('tapping the button increments the counter', (tester) async {
await tester.pumpWidget(const MaterialApp(home: CounterPage()));
expect(find.text('Count: 0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('Count: 1'), findsOneWidget);
});
}
pumpWidgetbuilds the widget;pumprenders the next frame after a change (pumpAndSettlewaits for animations to finish).- Finders locate widgets:
find.text,find.byIcon,find.byType,find.byKey. - Run them with
flutter test— no device needed.
Integration tests
The integration_test package drives the real app on a device or
emulator — use it for a handful of critical journeys, such as sign-up and
checkout.
Mocks
For verifying interactions ("was save called once?"), the mocktail
or mockito packages create mock objects. Hand-written fakes like the
one above are often simpler and clearer.