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)),
),
);
}
}