Widget 测试简介
了解有关 Flutter 中小部件测试的更多信息。
在 单元测试简介 菜谱中,你学习了如何使用 test 包测试 Dart 类。要测试小部件类,你需要由 Flutter SDK 提供的 flutter_test 包提供的几个额外的工具。
flutter_test 包为测试小部件提供以下工具
WidgetTester允许在测试环境中构建和与小部件交互。testWidgets()函数为每个测试用例自动创建一个新的WidgetTester,并用于代替正常的test()函数。Finder类允许在测试环境中搜索小部件。- 特定于小部件的
Matcher常量有助于验证Finder是否在测试环境中找到一个或多个小部件。
如果这听起来让人不知所措,请不要担心。在整个菜谱中学习所有这些部分如何协同工作,该菜谱使用以下步骤
- 添加
flutter_test依赖项。 - 创建一个要测试的小部件。
- 创建一个
testWidgets测试。 - 使用
WidgetTester构建小部件。 - 使用
Finder搜索小部件。 - 使用
Matcher验证小部件。
1. 添加 flutter_test 依赖项
#
在编写测试之前,将 flutter_test 依赖项包含在 pubspec.yaml 文件的 dev_dependencies 部分中。如果使用命令行工具或代码编辑器创建新的 Flutter 项目,则此依赖项应已就位。
dev_dependencies:
flutter_test:
sdk: flutter
2. 创建要测试的小部件
#接下来,创建一个用于测试的小部件。对于此菜谱,创建一个显示 title 和 message 的小部件。
class MyWidget extends StatelessWidget {
const MyWidget({super.key, required this.title, required this.message});
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. 创建 testWidgets 测试
#
有了要测试的小部件,首先编写你的第一个测试。使用 flutter_test 包提供的 testWidgets() 函数来定义测试。testWidgets 函数允许你定义一个小部件测试并创建一个 WidgetTester 来使用。
此测试验证 MyWidget 是否显示给定的标题和消息。它相应地命名,将在下一节中填充。
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', (tester) async {
// Test code goes here.
});
}
4. 使用 WidgetTester 构建小部件
#
接下来,使用 WidgetTester 提供的 pumpWidget() 方法在测试环境中构建 MyWidget。pumpWidget 方法构建并渲染提供的部件。
创建一个显示 "T" 作为标题和 "M" 作为消息的 MyWidget 实例。
void main() {
testWidgets('MyWidget has a title and message', (tester) async {
// Create the widget by telling the tester to build it.
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
});
}
关于 pump() 方法的说明
#在对 pumpWidget() 进行初始调用之后,WidgetTester 提供了重建相同小部件的其他方法。如果你正在使用 StatefulWidget 或动画,这将很有用。
例如,点击按钮会调用 setState(),但 Flutter 不会在测试环境中自动重建你的小部件。使用以下方法之一要求 Flutter 重建小部件。
-
tester.pump(Duration duration) -
安排一个帧并触发小部件的重建。如果指定了
Duration,它会将时钟向前推进该量并安排一个帧。即使持续时间超过单个帧,也不会安排多个帧。
-
tester.pumpAndSettle() -
使用给定的持续时间重复调用
pump(),直到不再安排任何帧。本质上,这将等待所有动画完成。
这些方法提供了对构建生命周期的细粒度控制,这在测试时特别有用。
5. 使用 Finder 搜索我们的小部件
#
在测试环境中有一个小部件后,使用 Finder 在小部件树中搜索 title 和 message Text 小部件。这允许验证小部件是否正确显示。
为此,使用 flutter_test 包提供的顶级 find() 方法来创建 Finders。由于你知道你在寻找 Text 小部件,因此使用 find.text() 方法。
有关 Finder 类的更多信息,请参阅 在小部件测试中查找小部件 菜谱。
void main() {
testWidgets('MyWidget has a title and message', (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. 使用 Matcher 验证小部件
#
最后,使用 flutter_test 提供的 Matcher 常量来验证屏幕上是否显示标题和消息 Text 小部件。Matcher 类是 test 包的核心部分,并提供了一种通用的方法来验证给定值是否满足期望。
确保小部件在屏幕上出现一次。为此,使用 findsOneWidget Matcher。
void main() {
testWidgets('MyWidget has a title and message', (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);
});
}
其他 Matcher
#除了 findsOneWidget 之外,flutter_test 还为常见情况提供了其他匹配器。
-
findsNothing 验证是否未找到任何小部件。
-
findsWidgets 验证是否找到一个或多个小部件。
-
findsNWidgets 验证是否找到特定数量的小部件。
-
matchesGoldenFile 验证小部件的渲染是否与特定的位图图像(“黄金文件”测试)匹配。
完整示例
#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', (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({super.key, required this.title, required this.message});
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)),
),
);
}
}