Widget 测试简介
在单元测试简介食谱中,您学习了如何使用 test 包测试 Dart 类。要测试 Widget 类,您需要 flutter_test 包提供的一些额外工具,该包随 Flutter SDK 一起提供。
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: flutter2. 创建一个待测试的 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 显示了指定的 title 和 message。它将被相应地命名,并在下一节中进行填充。
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 构建 Widget
#接下来,使用 WidgetTester 提供的 pumpWidget() 方法在测试环境中构建 MyWidget。pumpWidget 方法构建并渲染提供的 Widget。
创建一个 MyWidget 实例,该实例显示 "T" 作为 title 和 "M" 作为 message。
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 提供了其他方式来重建同一个 Widget。如果您正在处理 StatefulWidget 或动画,这会很有用。
例如,点击按钮会调用 setState(),但 Flutter 不会自动在测试环境中重建您的 Widget。使用以下方法之一要求 Flutter 重建 Widget。
tester.pump(Duration duration)- 调度一个帧并触发 Widget 的重建。如果指定了
Duration,它会将时钟向前推进该时间量并调度一个帧。即使持续时间长于单个帧,它也不会调度多个帧。
tester.pumpAndSettle()- 使用给定的持续时间重复调用
pump(),直到不再有任何计划中的帧。这基本上会等待所有动画完成。
这些方法提供了对构建生命周期的精细控制,这在测试时尤其有用。
5. 使用 Finder 查找我们的 Widget
#在测试环境中有了 Widget,就可以使用 Finder 搜索 Widget 树中的 title 和 message Text Widget。这允许验证 Widget 是否被正确显示。
为此,请使用 flutter_test 包提供的顶级 find() 方法来创建 Finders。由于您知道要查找 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'));
// Create the Finders.
final titleFinder = find.text('T');
final messageFinder = find.text('M');
});
}6. 使用 Matcher 验证 Widget
#最后,使用 flutter_test 提供的 Matcher 常量来验证 title 和 message 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');
// 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 还为常见情况提供了其他 Matcher。
findsNothing- 验证未找到任何 Widget。
findsWidgets- 验证找到一个或多个 Widget。
findsNWidgets- 验证找到指定数量的 Widget。
matchesGoldenFile- 验证 Widget 的渲染是否与特定的位图图像("golden file" 测试)匹配。
完整示例
#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)),
),
);
}
}