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 添加到 WidgetTraitasserts 列表参数中来定义部件的测试标准。然后这些断言应用于该特征的目标部件。

存在一些预定义的断言,但也可以使用 CustomTraitAssert 子类创建自定义断言。

一般断言

这些只涉及包含的特征,因此只针对目标部件。

例如:PositionAssertSizeAssertRelationAssert

相对断言

这些是相对于另一个引用特征的(因此隐式地相对于另一个部件)。引用特征必须指定 id 属性,而引用特征必须定义 traitId 属性。

例如:RelativePositionAssertRelativeSizeAssert

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

1 回复

更多关于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();
  });
});
回到顶部