在单元测试介绍 部分,我们学习了使用 test 包测试 Dart 类的方法。为了测试 Widget 类,我们需要使用 flutter_test 包提供的额外工具,这些工具是跟 Flutter SDK 一起发布的。
flutter_test 包提供了以下工具用于测试 Widget:
The
WidgetTester, which allows us to build and interact with Widgets in a test environment.WidgetTester,使用该工具可在测试环境下建立 Widget 并与其交互。The
testWidgetsfunction. This function will automatically create a newWidgetTesterfor each test case, and is used in place of the normaltestfunction.testWidgets函数,此函数会自动为每个测试创建一个WidgetTester,用来代替普通的test函数。Finderclasses. These allow us to search for Widgets in the test environment.Finder类,允许我们在测试环境下查找 Widget。Widget-specific
Matcherconstants, which help us verify whether aFinderlocates a Widget or multiple Widgets in the test environment.Widget-specific
Matcher常量,该常量在测试环境下帮助我们验证Finder是否定位到一个或多个 Widgets。
如果觉得太复杂,别担心!让我们通过示例把这些内容整合起来。
步骤:
添加一个
flutter_test依赖创建一个测试用的 Widget
创建一个
testWidgets测试方法使用
WidgetTester建立 Widget使用
Finder查找 Widget使用
Matcher验证 Widget 是否正常工作
一. 添加一个 flutter_test 依赖
我们开始编写测试之前,需要先给 pubspec.yaml 文件的 dev_dependencies 段添加 flutter_test 依赖。如果使用命令行或编译器新建一个 Flutter 项目,那么依赖已经默认添加了。
dev_dependencies:flutter_test:sdk: flutter
二. 创建一个测试用的 Widget
接下来,我们需要创建一个可以测试的 Widget!在此例中,我们创建了一个 Widget 显示一个标题和信息。
class MyWidget extends StatelessWidget {final String title;final String message;const MyWidget({Key key,@required this.title,@required this.message,}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',home: Scaffold(appBar: AppBar(title: Text(title),),body: Center(child: Text(message),),),);}}
三. 创建一个 testWidgets 测试方法
现在我们有了一个可以测试的 Widget,可以开始编写第一个测试了!第一步,我们用 flutter_test 包提供的 testWidgets 函数定义一个测试。testWidgets 函数可以定义一个 Widget 测试并创建一个可以使用的 WidgetTester。
我们的测试会验证 MyWidget 是否显示给定的标题和信息。
void main() {// Define a test. The TestWidgets function will also provide a WidgetTester// for us to work with. The WidgetTester will allow us to build and interact// with Widgets in the test environment.testWidgets('MyWidget has a title and message', (WidgetTester tester) async {// Test code will go here!});}
四. 使用 WidgetTester 建立 Widget
下一步,为了在测试环境中建立 MyWidget,我们可以使用 WidgetTester 提供的 pumpWidget 方法。pumpWidget 方法会建立并渲染我们提供的 Widget。
在这个示例中,我们将创建一个显示标题“T”和信息“M”的 MyWidget 示例。
void main() {testWidgets('MyWidget has a title and message', (WidgetTester tester) async {// Create the Widget tell the tester to build itawait tester.pumpWidget(MyWidget(title: 'T', message: 'M'));});}
备注
初次调用 pumpWidget 之后,WidgetTester 会提供其他方式来重建相同的 Widget。这对使用 StatefulWidget 或者动画会非常有用。
例如,如果我们点击调用 setState 的按钮,在测试环境中,Flutter 并不会自动重建你的 Widget。我们需要用以下列举的方法来让 Flutter 再一次建立我们的 Widget。
- tester.pump()
- Triggers a rebuild of the Widget after a given duration.
tester.pump():在一段给定时间后重建 Widget。
- 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.
- tester.pumpAndSettle():在给定期间内不断重复调用 pump 直到完成所有绘制帧。一般需要等到所有动画全部完成。这些方法在构建周期中保证细粒度控制,这在测试中非常有用。
五. 使用 Finder 查找 Widget
现在让我们在测试环境中建立 Widget。我们需要用 Finder 通过 Widget 树来查找 标题 和 信息 Text Widgets。这样可以验证这些 Widgets 是否正确显示。
在这个示例中,我们使用 flutter_test 包提供的顶级 find 方法来创建我们的 Finders。因为我们要查找的是 Text widgets,所以可以使用 find.text 方法。
关于 Finderclasses 的更多信息,请参阅 Finding Widgets in a Widget Test 章节。
void main() {testWidgets('MyWidget has a title and message', (WidgetTester tester) async {await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));// Create our Findersfinal titleFinder = find.text('T');final messageFinder = find.text('M');});}
六. 使用 Matcher 验证 Widget 是否正常工作
最后,让我们来用 flutter_test 提供的 Matcher 常量验证 Text Widgets 显示的标题和信息。Matcher 类是 test 包里的核心部分,它提供一种通用方法来验证给定值是否符合我们的预期。
在这个示例中,我们要确保 Widget 只在屏幕中出现一次。因此,可以使用 findsOneWidgetMatcher。
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 our// Text Widgets appear exactly once in the Widget treeexpect(titleFinder, findsOneWidget);expect(messageFinder, findsOneWidget);});}
其他的 Matchers
除了 findsOneWidget,flutter_test 还为常见情况提供了其他的 matchers。
- findsNothing
- verifies that no Widgets are found
findsNothing:验证没有可被查找的 Widgets。
- findsWidgets
- verifies one or more Widgets are found
findsWidgets:验证一个或多个 Widgets 被找到。
- findsNWidgets
- verifies a specific number of Widgets are found
- findsNWidgets:验证特定数量的 Widgets 被找到
完整示例:
import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';void main() {// Define a test. The TestWidgets function will also provide a WidgetTester// for us to work with. The WidgetTester will allow us to build and interact// with Widgets in the test environment.testWidgets('MyWidget has a title and message', (WidgetTester tester) async {// Create the Widget tell the tester to build itawait tester.pumpWidget(MyWidget(title: 'T', message: 'M'));// Create our Findersfinal titleFinder = find.text('T');final messageFinder = find.text('M');// Use the `findsOneWidget` matcher provided by flutter_test to verify our// Text Widgets appear exactly once in the Widget treeexpect(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);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',home: Scaffold(appBar: AppBar(title: Text(title),),body: Center(child: Text(message),),),);}}