Flutter自定义组件插件gherkin_widget_extension的使用
Flutter自定义组件插件gherkin_widget_extension的使用
目录
– 特性
- 在小部件测试环境中运行用 Gherkin 编写的测试。
- 提供一个名为
WidgetCucumberWorld
的CucumberWorld
,设计用于小部件测试,并且有存储和共享数据的桶。 - ✨ 支持无障碍:通过语义标签搜索小部件并检查其完整的语义。
- 提供一个 Json 加载器,帮助使用 Json 数据构建小部件。
- 测试失败时,截图和小部件树会转储到文件中。
- 为小部件测试量身定制的 Gherkin 报告器。
– 入门指南
🥒 添加 gherkin_widget_extension
依赖
在项目的 pubspec.yaml
文件中,在 dev_dependencies
中添加 gherkin_widget_extension
库:
gherkin_widget_extension: ^1.0.0
然后运行 pub get
下载依赖。
✏️ 编写场景
在 test
文件夹中创建一个 features
文件夹。如果不存在 test
文件夹,则创建它。在 features
文件夹中创建一个 .feature
文件,例如 counter.feature
并编写第一个场景:
Feature: Counter
The counter should be incremented when the button is pressed.
@my_tag
Scenario: Counter increases when the button is pressed
Given I launch the counter application
When I tap the "increment" button 10 times
Then I expect the "counter" to be "10"
下一步:实现步骤定义。
🔗 声明步骤定义
步骤定义类似于将 Gherkin 句法与小部件交互的代码之间的链接。通常,given
步骤用于设置测试上下文,when
步骤表示测试的主要动作(当用户验证表单时,当用户应用他的选择时,等等),而 then
步骤则断言屏幕上的一切(文本、状态、语义等)。
在 test
文件夹中,创建一个 step_definitions
文件夹并在该文件夹内创建一个 steps.dart
文件并开始实现步骤定义:
import 'package:gherkin/gherkin.dart';
import 'package:gherkin_widget_extension/gherkin_widget_extension.dart';
StepDefinitionGeneric<WidgetCucumberWorld> givenAFreshApp() {
return given<WidgetCucumberWorld>(
'I launch the counter application', (context) async {
// ...
});
}
💡 建议
为了更好地理解,一个好的实践建议根据 Gherkin 关键字分割步骤定义文件(所有 Given
步骤定义在一个文件 given_steps.dart
中,所有 When
步骤定义在一个文件 when_steps.dart
中,等等)。将这些文件组织成代表功能的文件夹是一个加分项。
⚙️ 添加一些配置
gherkin
提供了广泛的可定制属性,详细信息可在 这里 查看。
在 test
文件夹中,创建一个新的文件 test_setup.dart
来声明自己的测试配置:
TestConfiguration TestWidgetsConfiguration({
String featurePath = '*.feature',
}) {
return TestConfiguration()
..features = [Glob(featurePath)]
..hooks = [WidgetHooks(dumpFolderPath: 'widget_tests_report_folder')]
..order = ExecutionOrder.sequential
..stopAfterTestFailed = false
..reporters = [
WidgetStdoutReporter(),
WidgetTestRunSummaryReporter(),
XmlReporter(dirRoot: Directory.current.path)
]
..stepDefinitions = [
givenAFreshApp(),
whenButtonTapped(),
thenCounterIsUpdated()
]
..defaultTimeout =
const Duration(milliseconds: 60000 * 10);
}
包特有的特性
..hooks
该包提供了一个 WidgetHooks
类,实现了由 gherkin
包提供的 Hook
类。这个类处理小部件测试报告,如截图和小部件树渲染。
更多信息 在这里。
..reporters
gherkin
包提供的 Reporters
在此包中得到了增强:
WidgetStdoutReporter
: 打印每个 Gherkin 步骤及其状态到终端日志。WidgetTestRunSummaryReporter
: 在测试执行结束时打印整个测试执行的摘要到终端日志。XmlReporter
: 生成格式化的 XML 测试执行报告,适用于 GitLab CI 测试报告。
更多信息 在这里。
🧪 设置测试运行器
在 test
文件夹中创建一个新文件 widget_test_runner.dart
并调用测试运行器方法:
void main() {
testWidgetsGherkin('widget tests',
testConfiguration: TestWidgetsConfiguration(featurePath: "test/features/*.feature"));
}
在 test
文件夹中创建另一个文件 flutter_test_config.dart
并声明测试执行配置以启用字体加载(截图所需):
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
await loadAppFonts();
await testMain();
}
🪄 运行你的测试
打开终端并执行你之前创建的文件:
flutter test test/widget_test_runner.dart
你应该看到这样的日志:
🎬️ 让我们开始吧!
编写你需要的任何 Gherkin 场景,使用 Cucumber 标签来运行某些场景,并探索 gherkin
包提供的所有选项。
– 使用方法
🌍 WidgetCucumberWorld
的优势
gherkin
包暴露了一个 CucumberWorld
用于存储数据并在同一场景中的所有步骤之间共享它们。CucumberWorld
是唯一的:每个场景只创建一个,并在场景完成后销毁,无论其状态如何。更多关于 CucumberWorld
的目标的信息 在这里。
WidgetCucumberWorld
类继承自 gherkin
包中的 CucumberWorld
类,并公开以下项目:
WidgetTester tester
: 允许与小部件进行交互(点击、泵送、获取元素等),SemanticsHandle semantics
: 在测试开始时创建,使与Semantics
小部件进行交互成为可能,String scenarioName
: 存储当前场景名称 - 用于报告,String json
: 存储用于构建小部件的 Json 数据,Bucket bucket
: 存储来自步骤变量的测试数据,更多信息在下一节中。
无需创建 WidgetCucumberWorld
对象,包提供了一个名为 currentWorld
的对象,可以通过 context
对象访问:
context.world.tester;
🪣 数据桶
如果 CucumberWorld
是测试的背包,那么 Buckets
就是它的口袋。
Buckets
允许你在 CucumberWorld
中组织你的数据。确实,如果你的应用程序有不同的使用场景,比如票务销售、客户账户或交通信息等,你可能不希望将所有测试数据都存储在 CucumberWorld
中:你可以使用 Buckets
根据业务领域存储数据(例如:TicketSellBucket,AccountBucket,…)。
Buckets
是抽象和通用的,因此要使用它们,你需要创建一个实现 Bucket
类的类:
class ExampleBucket implements Bucket {
String? buttonAction;
int nbActions = 0;
}
在这个例子中,桶的名称是 ExampleBucket
并存储两个值:要交互的按钮的名称,以及该按钮被点击的次数。
在将数据存储到 Bucket
之前,它需要通过 WidgetCucumberWorld
对象 currentWorld
初始化:
currentWorld.bucket = ExampleBucket();
然后使用 currentWorld
设置和访问 Bucket
的数据:
currentWorld.readBucket<ExampleBucket>().nbActions = count;
请记住,需要指定 Bucket
的类型才能使用它并访问其数据(在这里是 <ExampleBucket>
)。
👁️ 不要忘记可访问性
在移动应用程序中,可访问性至关重要并且必须进行测试。这个包提供了一个测试小部件语义的方法:
Finder widgetWithSemanticLabel(
Type widgetType,
String semanticLabel,
{bool skipOffstage = true,
Matcher? semanticMatcher}
)
这个方法允许你通过其类型、语义标签和完整的语义查找一个控件:
final widgetToFind = find.widgetWithSemanticLabel(
Checkbox,
"Checkbox label",
semanticMatcher: matchesSemantics(
hasEnabledState: true,
label: "Checkbox label",
hasTapAction: true,
isEnabled: true,
isFocusable: true,
textDirection: TextDirection.ltr,
hasCheckedState: true,
isChecked: true
)
);
expect(widgetToFind, findsOneWidget);
expect
如果没有找到相应的控件会抛出 AssertionError
。
🧩 WidgetObject
模式和 TestWidgets
类的使用
根据“小部件对象”模式组织测试代码可以帮助维护和代码清晰度。“小部件对象”模式(为此包发明)强烈受到页面对象模式的启发,并应用于 Flutter 小部件测试。
在测试自动化中,页面对象模式要求为每个页面(或屏幕)创建一个类,不仅包含与该页面交互的所有方法(点击、检查等),还包括选择器到该页面(如何定位页面上的给定元素)。这样可以简化理解和维护。
在小部件测试上下文中,测试的是屏幕的一部分(或小部件树的一部分),而不是页面。但是,保持页面对象逻辑仍然是有用的。
抽象类 TestWidget
可以作为“小部件对象”类的接口来强制实现最重要的方法:
pumpItUp()
- 指示如何泵送要测试的小部件
示例:
class MyWidgetObject implements TestWidgets {
@override
Future<void> pumpItUp() async {
var widgetToPump = const MaterialTestWidget(
child: MyApp(),
);
pumpWidget(widgetToPump);
}
}
🔄 通过 JsonLoader
加载数据
应用程序经常使用从 API 获取的数据来构建屏幕上的组件,而小部件测试不能依赖于 API 及其潜在的缓慢性和不稳定性。小部件测试应该可靠且尽可能快。因此,API Json 响应可以存储到文件并通过提供的 JsonLoader
加载,以帮助构建要测试的小部件。
var jsonMap = await JsonLoader.loadJson("path/to/json/folder");
jsonMap
包含一个键为 Json 文件名、值为 Json 文件内容的映射。
📸 截图和小部件树渲染的钩子
📣 至少需要在 pubspec.yml
中声明一种字体以获得美观且易读的截图(否则将用方块代替字体)。
📣 flutter_test_config.dart
必须位于 test
目录的根目录下(参见 🧪 设置测试运行器 段落)。
钩子包含在 Cucumber 驱动的测试期间执行的方法(场景前后,步骤前后,…)。有关钩子的更多信息 在这里。
这个包提供了一个名为 WidgetHooks
的钩子以改进报告并提供更多的测试失败信息,例如截图和小部件树渲染。在 TestConfiguration
中添加此钩子以享受其功能:
TestConfiguration()
..hooks = [
WidgetHooks(dumpFolderPath:'widget_tests_report_folder')
]
参数 dumpFolderPath
是必需的
它代表在测试失败时截图和小部件渲染将被存储的报告文件夹。
📣 该包提供了一个名为 MaterialTestWidget
的自定义小部件。这个小部件必须封装要泵送的小部件以启用截图和小部件渲染。
📋 小部件测试报告器
MonochromePrinter
这个类扩展了 LogPrinter
类并简化了日志记录。它是其他提供的报告器的基础。Flutter 日志非常方便但可能在报告上下文中过于冗长。MonochromePrinter
允许你在日志控制台中打印消息而不带任何装饰/表情符号/任何东西。
WidgetStdoutReporter
这个报告器负责:
- 打印正在运行的场景及其文件位置的名称,
- 打印每个步骤及其状态和时间持续情况
√
如果步骤成功×
如果步骤失败-
如果步骤跳过
- 打印场景执行结果
- PASSED
- FAILED
- 处理文本着色
示例:
WidgetTestRunSummaryReporter
这个报告器负责打印测试执行摘要及其文本着色。它总结了:
- 预期场景的总数
- 成功场景的总数
- 失败场景的总数
- 跳过场景的总数
示例:
XmlReporter
这个报告器生成一个名为 junit-report.xml
的 XML 文件,格式为 JUnit,适用于 GitLab CI。此文件将在根目录中创建。
这样,所有测试执行结果都将出现在管道的 Tests
标签中:
测试执行的详细信息也可以查看:
在失败的情况下,System output
包含:
- 步骤的状态(通过、失败或跳过)的摘要
- 异常堆栈跟踪
- 小部件渲染的打印
在失败的情况下,还会提供指向截图的链接。
🦊 注意 GitLab Job 配置
将截图和 XML 文件作为工件公开,以便 GitLab CI 可以收集 Tests
标签中的信息。有关 GitLab 测试报告的更多信息 在这里。
添加报告器到测试配置
要利用提供的报告器,它们需要添加到 TestConfiguration
中:
TestConfiguration()
..reporters = [
WidgetStdoutReporter(),
WidgetTestRunSummaryReporter(),
XmlReporter(dirRoot:Directory.current.path)
]
更多关于Flutter自定义组件插件gherkin_widget_extension的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义组件插件gherkin_widget_extension的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用自定义组件插件 gherkin_widget_extension
的示例代码。假设 gherkin_widget_extension
是一个包含一些自定义小部件(widgets)的插件,并且你需要在你的Flutter项目中引用这些自定义小部件。
首先,确保你已经在 pubspec.yaml
文件中添加了 gherkin_widget_extension
依赖项:
dependencies:
flutter:
sdk: flutter
gherkin_widget_extension: ^x.y.z # 替换为实际的版本号
然后,运行 flutter pub get
来获取依赖项。
接下来,在你的Flutter项目中使用这个插件。假设 gherkin_widget_extension
包含一个名为 CustomButton
的自定义按钮小部件,下面是如何在你的代码中引用和使用它的示例:
import 'package:flutter/material.dart';
import 'package:gherkin_widget_extension/gherkin_widget_extension.dart'; // 导入插件
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Custom Widget Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 使用插件中的 CustomButton 小部件
CustomButton(
onPressed: () {
// 点击按钮时的回调
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Button clicked!')),
);
},
child: Text('Click Me'),
),
SizedBox(height: 20),
// 你可以在这里添加更多的自定义小部件
],
),
),
);
}
}
在这个示例中,我们:
- 在
pubspec.yaml
文件中添加了gherkin_widget_extension
依赖项。 - 在
MyApp
类中创建了一个基本的 Flutter 应用。 - 在
MyHomePage
类中创建了一个Scaffold
,并在其body
中使用Column
来布局子部件。 - 在
Column
中添加了CustomButton
小部件,并为其onPressed
属性提供了一个回调函数,当按钮被点击时,显示一个SnackBar
。
请注意,这个示例假设 CustomButton
是一个接受 onPressed
和 child
参数的自定义按钮小部件。具体的参数和用法可能会根据你实际使用的插件版本和文档有所不同。
务必查阅 gherkin_widget_extension
的官方文档或源码以获取准确的信息和用法示例。