在单元测试介绍 部分,我们学习了使用 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
testWidgets
function. This function will automatically create a newWidgetTester
for each test case, and is used in place of the normaltest
function.testWidgets
函数,此函数会自动为每个测试创建一个WidgetTester
,用来代替普通的test
函数。Finder
classes. These allow us to search for Widgets in the test environment.Finder
类,允许我们在测试环境下查找 Widget。Widget-specific
Matcher
constants, which help us verify whether aFinder
locates 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);
@override
Widget 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 it
await 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
方法。
关于 Finder
classes 的更多信息,请参阅 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 Finders
final titleFinder = find.text('T');
final messageFinder = find.text('M');
});
}
六. 使用 Matcher 验证 Widget 是否正常工作
最后,让我们来用 flutter_test
提供的 Matcher
常量验证 Text
Widgets 显示的标题和信息。Matcher
类是 test
包里的核心部分,它提供一种通用方法来验证给定值是否符合我们的预期。
在这个示例中,我们要确保 Widget 只在屏幕中出现一次。因此,可以使用 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 our
// Text Widgets appear exactly once in the Widget tree
expect(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 it
await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
// Create our Finders
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 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),
),
),
);
}
}