An introduction to widget testing
In the introduction to unit testing recipe,
you learned how to test Dart classes using the test package.
To test widget classes, you need a few additional tools provided by the
flutter_test package, which ships with the Flutter SDK.
The flutter_test package provides the following tools for
testing widgets:
- The
WidgetTesterallows building and interacting with widgets in a test environment. - The
testWidgets()function automatically creates a newWidgetTesterfor each test case, and is used in place of the normaltest()function. - The
Finderclasses allow searching for widgets in the test environment. - Widget-specific
Matcherconstants help verify whether aFinderlocates a widget or multiple widgets in the test environment.
If this sounds overwhelming, don’t worry. Learn how all of these pieces fit together throughout this recipe, which uses the following steps:
- Add the
flutter_testdependency. - Create a widget to test.
- Create a
testWidgetstest. - Build the widget using the
WidgetTester. - Search for the widget using a
Finder. - Verify the widget using a
Matcher.
1. Add the flutter_test dependency
Before writing tests, include the flutter_test
dependency in the dev_dependencies section of the pubspec.yaml file.
If creating a new Flutter project with the command line tools or
a code editor, this dependency should already be in place.
dev_dependencies:
flutter_test:
sdk: flutter
2. Create a widget to test
Next, create a widget for testing. For this recipe,
create a widget that displays a title and message.
class MyWidget extends StatelessWidget {
const MyWidget({
Key? key,
required this.title,
required this.message,
}) : super(key: key);
final String title;
final String message;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
}
3. Create a testWidgets test
With a widget to test, begin by writing your first test.
Use the testWidgets() function provided by the
flutter_test package to define a test.
The testWidgets function allows you to define a
widget test and creates a WidgetTester to work with.
This test verifies that MyWidget displays a given title and message.
It is titled accordingly, and it will be populated in the next section.
void main() {
// Define a test. The TestWidgets function also provides a WidgetTester
// to work with. The WidgetTester allows you to build and interact
// with widgets in the test environment.
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
// Test code goes here.
});
}
4. Build the widget using the WidgetTester
Next, build MyWidget inside the test environment by using the
pumpWidget() method provided by WidgetTester.
The pumpWidget method builds and renders the provided widget.
Create a MyWidget instance that displays “T” as the title
and “M” as the message.
void main() {
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
// Create the widget by telling the tester to build it.
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
});
}Notes about the pump() methods
After the initial call to pumpWidget(), the WidgetTester provides
additional ways to rebuild the same widget. This is useful if you’re
working with a StatefulWidget or animations.
For example, tapping a button calls setState(), but Flutter won’t
automatically rebuild your widget in the test environment.
Use one of the following methods to ask Flutter to rebuild the widget.
tester.pump(Duration duration)- Schedules a frame and triggers a rebuild of the widget.
If a
Durationis specified, it advances the clock by that amount and schedules a frame. It does not schedule multiple frames even if the duration is longer than a single frame.
tester.pumpAndSettle()- Repeatedly calls
pump()with the given duration until there are no longer any frames scheduled. This, essentially, waits for all animations to complete.
These methods provide fine-grained control over the build lifecycle, which is particularly useful while testing.
5. Search for our widget using a Finder
With a widget in the test environment, search
through the widget tree for the title and message
Text widgets using a Finder. This allows verification that
the widgets are being displayed correctly.
For this purpose, use the top-level find()
method provided by the flutter_test package to create the Finders.
Since you know you’re looking for Text widgets, use the
find.text() method.
For more information about Finder classes, see the
Finding widgets in a widget test recipe.
void main() {
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
// Create the Finders.
final titleFinder = find.text('T');
final messageFinder = find.text('M');
});
}
6. Verify the widget using a Matcher
Finally, verify the title and message Text widgets appear on screen
using the Matcher constants provided by flutter_test.
Matcher classes are a core part of the test package,
and provide a common way to verify a given
value meets expectations.
Ensure that the widgets appear on screen exactly one time.
For this purpose, use the findsOneWidget Matcher.
void main() {
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
final titleFinder = find.text('T');
final messageFinder = find.text('M');
// Use the `findsOneWidget` matcher provided by flutter_test to verify
// that the Text widgets appear exactly once in the widget tree.
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}Additional Matchers
In addition to findsOneWidget, flutter_test provides additional
matchers for common cases.
findsNothing- Verifies that no widgets are found.
findsWidgets- Verifies that one or more widgets are found.
findsNWidgets- Verifies that a specific number of widgets are found.
matchesGoldenFile- Verifies that a widget’s rendering matches a particular bitmap image (“golden file” testing).
Complete example
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
// Define a test. The TestWidgets function also provides a WidgetTester
// to work with. The WidgetTester allows building and interacting
// with widgets in the test environment.
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
// Create the widget by telling the tester to build it.
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
// Create the Finders.
final titleFinder = find.text('T');
final messageFinder = find.text('M');
// Use the `findsOneWidget` matcher provided by flutter_test to
// verify that the Text widgets appear exactly once in the widget tree.
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
class MyWidget extends StatelessWidget {
const MyWidget({
Key? key,
required this.title,
required this.message,
}) : super(key: key);
final String title;
final String message;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
}