Flutter布局测试插件layout_tester的使用
Flutter布局测试插件layout_tester的使用
该辅助包提供了用于布局和部件组合测试的简单接口。
安装
该包基于 flutter_test
SDK 包中的 WidgetTester
构建,因此仅在部件测试环境中使用。该包必须作为开发依赖项添加。
$ flutter pub add --dev layout_tester
有关测试的一般信息,请参阅:
概述
包中的核心类是 LayoutTester
,它提供了 testLayout
方法,该方法用于运行测试并期望以 WidgetTrait
形式输入一组数据。这些表示部件树中的部件,并用于制定关于布局特定属性的断言。
注意
免费方法 testLayout
可用作简写,以便调用隐式实例的 LayoutTester
。
数据结构
-
WidgetTrait
表示部件树中的特定部件,由
targetId
属性标识。它用于定义针对目标部件的断言。 -
TargetId
在部件树中标识一个目标部件,并主要由
WidgetTrait
使用。目标可以通过类型、键或两者结合来识别。还可以定义自定义分隔符。 -
PositionAssert
用于对目标部件的位置进行断言。
-
SizeAssert
用于对目标部件的大小进行断言。
-
RelationAssert
用于对目标部件的属性与指定值进行比较的断言。
-
RelativePositionAssert
用于对目标部件相对于特定对象的位置进行断言。
-
RelativeSizeAssert
用于对目标部件相对于另一个部件的大小进行断言。
-
CustomTraitAssert
创建自定义断言的模板。
缩写
断言类提供了不同的构造函数,以初始化所需属性。除了这种经典方式外,还有许多现成场景的简写生成器函数。要访问这些简写生成器函数,请包含包中的 assertion_factory
库。
更多库API的信息,请参阅文档。
使用
定义目标部件
为了定义要测试的目标部件,可以将 TargetId
类与 WidgetTrait
结合使用。TargetId
将不同属性组合成一个唯一标识符,内部转换为 WidgetFinder
。如果搜索结果有多个元素,则可以通过定义 elementIndex
字段来从集合中选择一个。这相当于查找器的 .at
方法。
// 默认ID构造函数。至少设置一个参数。
const TargetId(type: Container, key: Key('#'), elementIndex: 0)
// 自定义ID。
const TargetId.custom(
(widget) => widget is Container && widget.key == Key('#'),
elementIndex:0,
)
搜索上下文
默认情况下,搜索上下文为整个屏幕。这意味着目标部件将在屏幕上所有可用部件的集合中搜索。可以通过命名一个已存在的父部件来限制搜索范围。通过指定父部件特征,搜索上下文将限于该父部件,并且只会考虑其包含的部件。
注意
父部件不一定是目标部件的直接父部件。
// 部件树
await tester.pumpWidget(
Row(
children: [
SizedBox(width: 300),
Expanded(
child: Center(
// 目标
child: SizedBox(width: 50, height: 50),
),
),
],
),
);
// 不指定父上下文。
testLayout(
tester,
{
// 必须指定元素索引。
WidgetTrait(
targetId: const TargetId(type: SizedBox, elementIndex: 1),
),
},
);
// 指定父上下文。
testLayout(
tester,
{
// 父部件
WidgetTrait(
targetId: const TargetId(type: Expanded),
descendants: [
// 不需要指定元素索引,因为父上下文是唯一的。
WidgetTrait(targetId: const TargetId(type: SizedBox)),
],
),
}
);
定义测试标准
通过将 TraitAsserts
添加到 WidgetTrait
的 asserts
列表参数中来定义部件的测试标准。然后这些断言应用于该特征的目标部件。
存在一些预定义的断言,但也可以使用 CustomTraitAssert
子类创建自定义断言。
一般断言
这些只涉及包含的特征,因此只针对目标部件。
例如:PositionAssert
,SizeAssert
,RelationAssert
相对断言
这些是相对于另一个引用特征的(因此隐式地相对于另一个部件)。引用特征必须指定 id
属性,而引用特征必须定义 traitId
属性。
例如:RelativePositionAssert
,RelativeSizeAssert
testLayout(
tester,
{
// 引用特征
WidgetTrait(
id: 'trait0',
targetId: const TargetId(key: Key('#')),
),
// 引用特征
WidgetTrait(
targetId: const TargetId(type: Text),
asserts: [
RelativePositionAssert.target(
traitId: 'trait0',
left: 25,
)
],
)
},
);
高级
定义布局测试的标准就像根据部件树建模一样简单,但只考虑相关的元素。
示例:
testWidgets('example', (tester) async {
await tester.pumpWidget(
// 元素1
Container(
// 元素2
child: Center(
// 元素3
child: Container(
key: const Key('e2'),
width: 500,
height: 500,
// 元素4
child: Center(
// 元素5
child: Container(width: 50, height: 50),
),
),
),
),
);
testLayout(
tester,
{
// 描述元素1,但未定义断言。
WidgetTrait(
targetId: const TargetId(type: Container, elementIndex: 0),
descendants: [
// 描述元素3,但未定义断言。
WidgetTrait(
id: 'e2',
targetId: const TargetId(key: Key('e2')),
descendants: [
// 描述元素5并定义断言。
WidgetTrait(
targetId: const TargetId(type: Container),
asserts: const [
// 元素5必须是50x50尺寸。
SizeAssert.symmetric(50),
// 元素5必须是元素'e1'(元素1)的0.1倍大小。
RelativeSizeAssert.symmetric(
traitId: 'e2',
0.1,
),
],
),
],
),
],
),
},
);
});
如上所示,测试数据结构在某种程度上类似于部件树。这种结构不是强制性的,但它使布局测试器能够识别部件的层次顺序,并在某些情况下允许省略细节。
在上面的例子中,对于元素1的特征,必须使用 elementIndex
属性来精确识别部件,因为在树中有多个 Container
实例,这使得测试器无法知道应该考虑哪一个。对于元素5来说,不需要索引,因为它作为两个其他 Container
的子元素的位置已经足够。
如果父部件的特征不定义断言,可以使用简写记法来创建元素。
示例
testLayout(
tester,
{
// 元素5
WidgetTrait.withParents(
// 定义父ID,这些ID被转换为特征层次结构。
// 第一个是直接父部件,最后一个是最顶层父部件。
const [
// 表示元素3
ComposeTargetId(type: Container, elementIndex: 0),
// 表示元素1
ComposeTargetId(key: Key('e2'), traitId: 'e2'),
],
targetId: const TargetId(type: Container),
asserts: const [
// 元素5必须是50x50尺寸。
SizeAssert.symmetric(50),
// 元素5必须是元素'e1'(元素1)的0.1倍大小。
const RelativeSizeAssert.symmetric(
traitId: 'e2',
0.1,
),
],
),
},
);
更多关于Flutter布局测试插件layout_tester的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter布局测试插件layout_tester的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
layout_tester
是一个用于测试 Flutter 布局的插件,它可以帮助开发者验证 UI 布局是否符合预期。通过编写测试用例,你可以确保在不同设备、屏幕尺寸和方向下,你的应用布局都能正确显示。
以下是使用 layout_tester
插件的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 layout_tester
依赖:
dev_dependencies:
layout_tester: ^0.1.0
然后运行 flutter pub get
来获取依赖。
2. 导入包
在你的测试文件中导入 layout_tester
包:
import 'package:layout_tester/layout_tester.dart';
import 'package:flutter_test/flutter_test.dart';
3. 编写测试用例
你可以编写测试用例来验证布局。以下是一个简单的例子,测试一个 Container
的宽度是否符合预期:
void main() {
testWidgets('Container width test', (WidgetTester tester) async {
// 创建一个 Container,宽度为 100
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
),
);
// 使用 layout_tester 验证 Container 的宽度
final containerFinder = find.byType(Container);
expect(
LayoutTester.getSize(containerFinder).width,
equals(100),
);
});
}
4. 运行测试
你可以使用 flutter test
命令来运行测试:
flutter test
5. 测试不同屏幕尺寸和方向
layout_tester
还允许你测试不同屏幕尺寸和方向下的布局。例如:
testWidgets('Container width test on different screen sizes', (WidgetTester tester) async {
// 设置屏幕尺寸为 400x800
tester.binding.window.physicalSizeTestValue = const Size(400, 800);
tester.binding.window.devicePixelRatioTestValue = 1.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
),
);
// 验证 Container 的宽度
final containerFinder = find.byType(Container);
expect(
LayoutTester.getSize(containerFinder).width,
equals(100),
);
// 恢复默认屏幕尺寸
addTearDown(() {
tester.binding.window.clearPhysicalSizeTestValue();
tester.binding.window.clearDevicePixelRatioTestValue();
});
});