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
WidgetTester
allows building and interacting with widgets in a test environment. -
The
testWidgets()
function automatically creates a newWidgetTester
for each test case, and is used in place of the normaltest()
function. -
The
Finder
classes allow searching for widgets in the test environment. -
Widget-specific
Matcher
constants help verify whether aFinder
locates 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_test
dependency. - Create a widget to test.
-
Create a
testWidgets
test. -
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 {
final String title;
final String message;
const MyWidget({
Key key,
@required this.title,
@required this.message,
}) : super(key: key);
@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.
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(MyWidget(title: 'T', message: 'M'));
});
}
Note
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.
Triggers a rebuild of the widget after a given duration. |
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(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(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. |
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(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 {
final String title;
final String message;
const MyWidget({
Key key,
@required this.title,
@required this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
}