跳到主內容

使用整合測試衡量效能

如何為 Flutter 應用進行效能分析。

在移動應用方面,效能對使用者體驗至關重要。使用者期望應用具有流暢的滾動和有意義的動畫,沒有卡頓或跳幀,這被稱為“卡頓”。如何確保您的應用在各種裝置上都沒有卡頓?

有兩種選擇:首先,手動在不同裝置上測試應用。雖然對於較小的應用,這種方法可能有效,但隨著應用規模的擴大,它會變得更加繁瑣。或者,執行一個執行特定任務並記錄效能時間線的整合測試。然後,檢查結果以確定應用中的特定部分是否需要改進。

在本教程中,學習如何編寫一個記錄效能時間線的同時執行特定任務並將結果摘要儲存到本地檔案的測試。

本示例將採取以下步驟

  1. 編寫一個滾動列表項的測試。
  2. 記錄應用的效能。
  3. 將結果儲存到磁碟。
  4. 執行測試。
  5. 檢查結果。

1. 編寫一個滾動列表項的測試

#

在本教程中,記錄應用在滾動列表項時的效能。為了專注於效能分析,本教程基於小部件測試中的 滾動 教程。

按照該教程中的說明建立一個應用並編寫一個測試來驗證一切是否按預期工作。

2. 記錄應用的效能

#

接下來,記錄應用在滾動列表時的效能。使用 traceAction() 方法執行此任務,該方法由 IntegrationTestWidgetsFlutterBinding 類提供。

此方法執行提供的函式並記錄一個 Timeline,其中包含有關應用效能的詳細資訊。此示例提供了一個滾動列表項的函式,以確保顯示特定項。當函式完成時,traceAction() 建立一個報告資料 Map,其中包含 Timeline

當執行多個 traceAction 時,指定 reportKey。預設情況下,所有 Timelines 均使用鍵 timeline 儲存,在本示例中,reportKey 已更改為 scrolling_timeline

dart
await binding.traceAction(() async {
  // Scroll until the item to be found appears.
  await tester.scrollUntilVisible(
    itemFinder,
    500.0,
    scrollable: listFinder,
  );
}, reportKey: 'scrolling_timeline');

3. 將結果儲存到磁碟

#

現在您已經捕獲了效能時間線,您需要一種方法來檢視它。Timeline 物件提供有關發生的所有事件的詳細資訊,但它沒有提供一種方便的方法來檢視結果。

因此,將 Timeline 轉換為 TimelineSummaryTimelineSummary 可以執行兩項任務,使檢視結果更容易

  1. 將包含在 Timeline 中的資料的 json 文件寫入磁碟。此摘要包括有關跳過的幀數、最慢的構建時間等資訊。
  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>,
        );

        // Convert the Timeline into a TimelineSummary that's easier to
        // read and understand.
        final summary = driver.TimelineSummary.summarize(timeline);

        // Then, write the entire timeline to disk in a json format.
        // This file can be opened in the Chrome browser's tracing tools
        // found by navigating to chrome://tracing.
        // Optionally, save the summary to disk by setting includeSummary
        // to 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 選項意味著為“profile 模式”而不是“debug 模式”編譯應用,以便基準測試結果更接近終端使用者將體驗到的結果。

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 {
    // Build our app and trigger a frame.
    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 {
      // Scroll until the item to be found appears.
      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>,
        );

        // Convert the Timeline into a TimelineSummary that's easier to
        // read and understand.
        final summary = driver.TimelineSummary.summarize(timeline);

        // Then, write the entire timeline to disk in a json format.
        // This file can be opened in the Chrome browser's tracing tools
        // found by navigating to chrome://tracing.
        // Optionally, save the summary to disk by setting includeSummary
        // to true
        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}