Widget 测试简介
在单元测试简介中,你学习了如何使用 test
包测试 Dart 类。要测试 Widget 类,你需要 Flutter SDK 附带的 flutter_test
包提供的几个额外工具。
flutter_test
包提供了以下用于测试 Widget 的工具:
WidgetTester
允许在测试环境中构建和与 Widget 交互。testWidgets()
函数为每个测试用例自动创建一个新的WidgetTester
,并用于替换普通的test()
函数。Finder
类允许在测试环境中搜索 Widget。- Widget 特定的
Matcher
常量有助于验证Finder
是否在测试环境中定位到一个或多个 Widget。
如果这听起来让人不知所措,别担心。在本教程中,你将学习所有这些部分是如何组合在一起的,它包含以下步骤:
- 添加
flutter_test
依赖项。 - 创建要测试的 Widget。
- 创建
testWidgets
测试。 - 使用
WidgetTester
构建 Widget。 - 使用
Finder
搜索 Widget。 - 使用
Matcher
验证 Widget。
1. 添加 flutter_test
依赖项
#在编写测试之前,请在 pubspec.yaml
文件的 dev_dependencies
部分包含 flutter_test
依赖项。如果使用命令行工具或代码编辑器创建新的 Flutter 项目,则此依赖项应该已经存在。
dev_dependencies:
flutter_test:
sdk: flutter
2. 创建要测试的 Widget
#接下来,创建一个 Widget 用于测试。在本教程中,创建一个显示 title
和 message
的 Widget。
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
测试
#有了要测试的 Widget 后,开始编写你的第一个测试。使用 flutter_test
包提供的 testWidgets()
函数来定义测试。testWidgets
函数允许你定义 Widget 测试并创建一个 WidgetTester
来使用。
此测试验证 MyWidget
是否显示给定的标题和消息。它已相应命名,将在下一节中填充。
void main() {
// 定义一个测试。TestWidgets 函数也提供了一个 WidgetTester 来使用。
// WidgetTester 允许你在测试环境中构建和与 Widget 交互。
testWidgets('MyWidget has a title and message', (tester) async {
// 测试代码写在这里。
});
}
4. 使用 WidgetTester
构建 Widget
#接下来,使用 WidgetTester
提供的 pumpWidget()
方法在测试环境中构建 MyWidget
。pumpWidget
方法构建并呈现提供的 Widget。
创建一个 MyWidget
实例,显示 "T" 作为标题,"M" 作为消息。
void main() {
testWidgets('MyWidget has a title and message', (tester) async {
// 通过告诉测试器构建它来创建 Widget。
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
});
}
关于 pump() 方法的说明
#在初始调用 pumpWidget()
之后,WidgetTester
提供了重建相同 Widget 的其他方法。如果你正在使用 StatefulWidget
或动画,这将非常有用。
例如,点击按钮会调用 setState()
,但 Flutter 不会自动在测试环境中重建你的 Widget。使用以下方法之一来请求 Flutter 重建 Widget。
tester.pump(Duration duration)
- 安排一个帧并触发 Widget 的重建。如果指定了
Duration
,它会将时钟提前该量并安排一个帧。即使持续时间长于单个帧,它也不会安排多个帧。
tester.pumpAndSettle()
- 使用给定的持续时间重复调用
pump()
,直到不再安排任何帧。这实际上是在等待所有动画完成。
这些方法提供了对构建生命周期的细粒度控制,这在测试时特别有用。
5. 使用 Finder
搜索我们的 Widget
#在测试环境中有了 Widget 后,使用 Finder
在 Widget 树中搜索 title
和 message
文本 Widget。这允许验证 Widget 是否正在正确显示。
为此,使用 flutter_test
包提供的顶级 find()
方法来创建 Finder
。由于你知道你在寻找 Text
Widget,因此使用 find.text()
方法。
有关 Finder
类的更多信息,请参阅在 Widget 测试中查找 Widget 教程。
void main() {
testWidgets('MyWidget has a title and message', (tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
// 创建 Finder。
final titleFinder = find.text('T');
final messageFinder = find.text('M');
});
}
6. 使用 Matcher
验证 Widget
#最后,使用 flutter_test
提供的 Matcher
常量验证标题和消息 Text
Widget 是否出现在屏幕上。Matcher
类是 test
包的核心部分,并提供了一种通用的方法来验证给定值是否符合预期。
确保 Widget 恰好出现一次。为此,使用 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');
// 使用 flutter_test 提供的 `findsOneWidget` matcher 来验证
// Text Widget 是否在 Widget 树中恰好出现一次。
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
其他 Matcher
#除了 findsOneWidget
之外,flutter_test
还为常见情况提供了其他 matcher。
findsNothing
- 验证没有找到 Widget。
findsWidgets
- 验证找到一个或多个 Widget。
findsNWidgets
- 验证找到特定数量的 Widget。
matchesGoldenFile
- 验证 Widget 的渲染是否与特定位图图像(“黄金文件”测试)匹配。
##完整示例
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
// 定义一个测试。TestWidgets 函数也提供了一个 WidgetTester 来使用。
// WidgetTester 允许你在测试环境中构建和与 Widget 交互。
testWidgets('MyWidget has a title and message', (tester) async {
// 通过告诉测试器构建它来创建 Widget。
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
// 创建 Finder。
final titleFinder = find.text('T');
final messageFinder = find.text('M');
// 使用 flutter_test 提供的 `findsOneWidget` matcher 来
// 验证 Text Widget 是否在 Widget 树中恰好出现一次。
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),
),
),
);
}
}
除非另有说明,否则本网站上的文档反映的是 Flutter 的最新稳定版本。页面最后更新于 2025-01-30。 查看源代码 或 报告问题。