Skip to main content

使用集成测试测量性能

对于移动应用而言,性能对用户体验至关重要。用户期望应用具有流畅的滚动和富有意义的动画,不会出现卡顿或丢帧(称为“卡顿”)。如何确保您的应用在各种设备上都能避免卡顿?

有两种选择:首先,在不同的设备上手动测试应用。虽然这种方法可能适用于较小的应用,但随着应用规模的增长,它会变得越来越繁琐。或者,运行一个执行特定任务并记录性能时间线的集成测试。然后,检查结果以确定是否需要改进应用的特定部分。

在本教程中,学习如何编写一个在执行特定任务时记录性能时间线并将结果摘要保存到本地文件的测试。

本教程使用以下步骤:

  1. 编写一个滚动浏览项目列表的测试。
  2. 记录应用的性能。
  3. 将结果保存到磁盘。
  4. 运行测试。
  5. 查看结果。

1. 编写一个滚动浏览项目列表的测试

#

在本教程中,记录应用在滚动浏览项目列表时的性能。为了专注于性能分析,本教程基于窗口部件测试中的滚动 教程。

按照该教程中的说明创建应用并编写测试以验证一切按预期工作。

2. 记录应用的性能

#

接下来,记录应用在滚动浏览列表时的性能。使用IntegrationTestWidgetsFlutterBinding 类提供的traceAction() 方法执行此任务。

此方法运行提供的函数并记录一个包含有关应用性能详细信息的Timeline。此示例提供了一个滚动浏览项目列表的函数,确保显示特定项目。当函数完成时,traceAction() 创建一个包含 Timeline 的报告数据 Map

运行多个 traceAction 时,请指定 reportKey。默认情况下,所有 Timeline 都使用键 timeline 存储,在此示例中,reportKey 更改为 scrolling_timeline

dart
await binding.traceAction(
  () async {
    // 滚动直到要查找的项目出现。
    await tester.scrollUntilVisible(
      itemFinder,
      500.0,
      scrollable: listFinder,
    );
  },
  reportKey: 'scrolling_timeline',
);

3. 将结果保存到磁盘

#

现在您已经捕获了性能时间线,您需要一种方法来查看它。Timeline 对象提供了有关发生的所有事件的详细信息,但它没有提供查看结果的便捷方法。

因此,将 Timeline 转换为TimelineSummaryTimelineSummary 可以执行两个任务,使查看结果更容易:

  1. 在磁盘上写入一个 json 文档,总结 Timeline 中包含的数据。此摘要包括有关丢帧数、最慢构建时间等的的信息。
  2. 将完整的 Timeline 作为 json 文件保存到磁盘。此文件可以使用 Chrome 浏览器的跟踪工具打开,该工具位于 chrome://tracing

要捕获结果,在 test_driver 文件夹中创建一个名为 perf_driver.dart 的文件,并添加以下代码:

dart
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() {
  return integrationDriver(
    responseDataCallback: (data) async {
      if (data != null) {
        final timeline = driver.Timeline.fromJson(
          data['scrolling_timeline'] as Map<String, dynamic>,
        );

        // 将 Timeline 转换为更容易阅读和理解的 TimelineSummary。
        final summary = driver.TimelineSummary.summarize(timeline);

        // 然后,将整个时间线以 json 格式写入磁盘。
        // 此文件可以在 Chrome 浏览器的跟踪工具中打开,
        // 方法是导航到 chrome://tracing。
        // 可选地,通过将 includeSummary 设置为 true 来将摘要保存到磁盘
        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}

integrationDriver 函数具有一个 responseDataCallback,您可以对其进行自定义。默认情况下,它将结果写入 integration_response_data.json 文件,但您可以自定义它以生成摘要,如本例所示。

4. 运行测试

#

在配置测试以捕获性能 Timeline 并将结果摘要保存到磁盘后,使用以下命令运行测试:

flutter drive \
  --driver=test_driver/perf_driver.dart \
  --target=integration_test/scrolling_test.dart \
  --profile

--profile 选项表示编译应用以用于“概要模式”而不是“调试模式”,以便基准结果更接近最终用户体验。

5. 查看结果

#

测试成功完成之后,项目的根目录下的 build 目录包含两个文件:

  1. scrolling_summary.timeline_summary.json 包含摘要。使用任何文本编辑器打开该文件以查看其中包含的信息。使用更高级的设置,您可以每次测试运行时保存一个摘要并创建结果图表。
  2. scrolling_timeline.timeline.json 包含完整的时间线数据。使用 Chrome 浏览器的跟踪工具(位于 chrome://tracing)打开该文件。跟踪工具提供了一个方便的界面来检查时间线数据,以发现性能问题的根源。

摘要示例

#
json
{
  "average_frame_build_time_millis": 4.2592592592592595,
  "worst_frame_build_time_millis": 21.0,
  "missed_frame_build_budget_count": 2,
  "average_frame_rasterizer_time_millis": 5.518518518518518,
  "worst_frame_rasterizer_time_millis": 51.0,
  "missed_frame_rasterizer_budget_count": 10,
  "frame_count": 54,
  "frame_build_times": [
    6874,
    5019,
    3638
  ],
  "frame_rasterizer_times": [
    51955,
    8468,
    3129
  ]
}

##完整示例

integration_test/scrolling_test.dart

dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:your_package/main.dart';

void main() {
  final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Counter increments smoke test', (tester) async {
    // 构建我们的应用并触发一帧。
    await tester.pumpWidget(MyApp(
      items: List<String>.generate(10000, (i) => 'Item $i'),
    ));

    final listFinder = find.byType(Scrollable);
    final itemFinder = find.byKey(const ValueKey('item_50_text'));

    await binding.traceAction(
      () async {
        // 滚动直到要查找的项目出现。
        await tester.scrollUntilVisible(
          itemFinder,
          500.0,
          scrollable: listFinder,
        );
      },
      reportKey: 'scrolling_timeline',
    );
  });
}

test_driver/perf_driver.dart

dart
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() {
  return integrationDriver(
    responseDataCallback: (data) async {
      if (data != null) {
        final timeline = driver.Timeline.fromJson(
          data['scrolling_timeline'] as Map<String, dynamic>,
        );

        // 将 Timeline 转换为更容易阅读和理解的 TimelineSummary。
        final summary = driver.TimelineSummary.summarize(timeline);

        // 然后,将整个时间线以 json 格式写入磁盘。
        // 此文件可以在 Chrome 浏览器的跟踪工具中打开,
        // 方法是导航到 chrome://tracing。
        // 可选地,通过将 includeSummary 设置为 true 来将摘要保存到磁盘
        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}