Widget 测试介绍
步骤:
Directions
一. 添加一个 flutter_test 依赖
1. Add the flutter_test dependency
二. 创建一个测试用的 Widget
2. Create a widget to test
三. 创建一个 testWidgets 测试方法
3. Create a testWidgets test
四. 使用 WidgetTester 建立 Widget
4. Build the widget using the WidgetTester
五. 使用 Finder 查找 widget
5. Search for our widget using a Finder
六. 使用 Matcher 验证 widget 是否正常工作
6. Verify the widget using a Matcher
完整样例
Complete example
在 单元测试介绍 部分,我们学习了使用 test
这个 package 测试 Dart 类的方法。为了测试 widget 类,我们需要使用 flutter_test
package 提供的额外工具,这些工具是跟 Flutter SDK 一起发布的。
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.
flutter_test
package 提供了以下工具用于 widget 的测试:
The flutter_test
package provides the following tools for
testing widgets:
-
WidgetTester
,使用该工具可在测试环境下建立 widget 并与其交互。The
WidgetTester
allows building and interacting with widgets in a test environment. -
testWidgets()
函数,此函数会自动为每个测试创建一个WidgetTester
,用来代替普通的test
函数。The
testWidgets()
function automatically creates a newWidgetTester
for each test case, and is used in place of the normaltest()
function. -
Finder
类,可以方便我们在测试环境下查找 widgets。The
Finder
classes allow searching for widgets in the test environment. -
Widget-specific
Matcher
常量,该常量在测试环境下帮助我们验证Finder
是否定位到一个或多个 widgets。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:
步骤:
Directions
-
添加一个
flutter_test
依赖Add the
flutter_test
dependency. -
创建一个测试用的 widget
Create a widget to test.
-
创建一个
testWidgets
测试方法Create a
testWidgets
test. -
使用
WidgetTester
建立 widgetBuild the widget using the
WidgetTester
. -
使用
Finder
查找 widgetSearch for the widget using a
Finder
. -
使用
Matcher
验证 widget 是否正常工作Verify the widget using a
Matcher
.
flutter_test
依赖
一. 添加一个
flutter_test
dependency
1. Add the 我们开始编写测试之前,需要先给 pubspec.yaml
文件的 dev_dependencies
段添加 flutter_test
依赖。如果使用命令行或编译器新建一个 Flutter 项目,那么依赖已经默认添加了。
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
二. 创建一个测试用的 Widget
2. Create a widget to test
接下来,我们需要创建一个可以测试的 widget!在此例中,我们创建了一个 widget 显示一个 标题 (title)
和 信息 (message)
。
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), ), ), ); } }
testWidgets
测试方法
三. 创建一个
testWidgets
test
3. Create a 现在我们有了一个可以测试的 widget,可以开始编写第一个测试了!第一步,我们用 flutter_test
这个 package 提供的
testWidgets()
函数定义一个测试。
testWidgets
函数可以定义一个 widget 测试并创建一个可以使用的 WidgetTester
。
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.
我们的测试会验证 MyWidget
是否显示给定的标题和信息。
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. }); }
WidgetTester
建立 Widget
四. 使用
WidgetTester
4. Build the widget using the 下一步,为了在测试环境中建立 MyWidget
,我们可以使用 WidgetTester
提供的
pumpWidget()
方法,pumpWidget
方法会建立并渲染我们提供的 widget。
Next, build MyWidget
inside the test environment by using the
pumpWidget()
method provided by WidgetTester
.
The pumpWidget
method builds and renders the provided widget.
在这个示例中,我们将创建一个显示标题“T”和信息“M”的 MyWidget
示例。
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')); }); }
pump() 方法相关提示
Notes about the pump() methods
初次调用 pumpWidget()
之后,WidgetTester
会提供其他方式来重建相同的 widget。这对使用 StatefulWidget
或者动画会非常有用。
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.
例如,如果我们点击调用 setState()
的按钮,在测试环境中,Flutter
并不会自动重建你的 widget。我们需要用以下列举的方法来让 Flutter
再一次建立我们的 widget。
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)
调度一帧以触发 widget 的重建。若指定了 Duration
,那么则会让 clock 经过该时长之后调度一帧。它并不会因为持续时间大于一帧的时间而调度多帧。
tester.pump(Duration duration)
Schedules a frame and triggers a rebuild of the widget.
If a Duration
is 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()
在给定期间内不断重复调用 pump 直到完成所有绘制帧。一般需要等到所有动画全部完成。
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.
Finder
查找 widget
五. 使用
Finder
5. Search for our widget using a 现在让我们在测试环境中建立 widget。我们需要用 Finder
通过 widget 树来查找
标题
和 信息
Text widgets,这样可以验证这些 Widgets 是否正确显示。
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.
为了实现这个目的,我们使用 flutter_test
这个 package 提供的顶级
find()
方法来创建我们的 Finders
。因为我们要查找的是 Text
widgets,所以可以使用 find.text()
方法。
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.
关于 Finder
classes 的更多信息,请参阅 定位到目标 Widgets 章节。
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'); }); }
Matcher
验证 widget 是否正常工作
六. 使用
Matcher
6. Verify the widget using a 最后,让我们来用 flutter_test
提供的 Matcher
常量验证 Text
widgets
显示的标题和信息。Matcher
类是 test
包里的核心部分,它提供一种通用方法来验证给定值是否符合我们的预期。
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.
在这个示例中,我们要确保 widget 只在屏幕中出现一次。因此,可以使用 findsOneWidget
Matcher
。
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); }); }
其他的 Matchers
Additional Matchers
除了 findsOneWidget
,flutter_test
还为常见情况提供了其他的 matchers。
In addition to findsOneWidget
, flutter_test
provides additional
matchers for common cases.
findsNothing
验证没有可被查找的 widgets。
findsNothing
Verifies that no widgets are found.
findsWidgets
验证一个或多个 widgets 被找到。
findsWidgets
Verifies that one or more widgets are found.
findsNWidgets
验证特定数量的 widgets 被找到。
findsNWidgets
Verifies that a specific number of widgets are found.
matchesGoldenFile
验证渲染的 widget 是否与特定的图像匹配(「目标文件」测试)。
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), ), ), ); } }