使用整合測試衡量效能
如何對 Flutter 應用進行效能分析。
對於移動應用而言,效能對使用者體驗至關重要。使用者期望應用能夠平滑滾動,並擁有流暢、無卡頓或掉幀(即所謂的“jank”)的動畫效果。如何確保你的應用在各種裝置上都不會出現卡頓?
有兩種選擇:首先,在不同裝置上手動測試應用。雖然這種方法對於小型應用可能有效,但隨著應用規模的擴大,它會變得越來越繁瑣。或者,執行一個執行特定任務並記錄效能時間線的整合測試。然後,檢查結果以確定應用的特定部分是否需要改進。
在本方案中,你將學習如何編寫一個在執行特定任務時記錄效能時間線,並將結果摘要儲存到本地檔案的測試。
本示例將採取以下步驟
- 編寫一個滾動瀏覽專案列表的測試。
- 記錄應用的效能。
- 將結果儲存到磁碟。
- 執行測試。
- 檢視結果。
1. 編寫一個滾動瀏覽專案列表的測試
#在本方案中,我們將記錄應用在滾動瀏覽專案列表時的效能。為了專注於效能分析,本方案建立在 Widget 測試的滾動方案基礎之上。
按照該方案中的說明建立一個應用,並編寫一個測試來驗證一切是否按預期工作。
2. 記錄應用的效能
#接下來,記錄應用在列表滾動時的效能。使用 IntegrationTestWidgetsFlutterBinding 類提供的 traceAction() 方法來執行此任務。
此方法執行提供的函式並記錄一個包含應用效能詳細資訊的 Timeline。此示例提供了一個滾動瀏覽專案列表的函式,確保顯示特定的專案。當函式執行完成後,traceAction() 會建立一個包含 Timeline 的報告資料 Map。
在執行多個 traceAction 時,需要指定 reportKey。預設情況下,所有 Timeline 都使用鍵 timeline 儲存,在此示例中,reportKey 被更改為 scrolling_timeline。
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 轉換為 TimelineSummary。TimelineSummary 可以執行兩項使檢視結果更容易的任務:
- 將包含在
Timeline中的資料摘要寫入磁碟上的 JSON 文件。該摘要包含有關掉幀數量、最慢構建時間等資訊。 - 將完整的
Timeline儲存為磁碟上的 JSON 檔案。此檔案可以使用 Chrome 瀏覽器的跟蹤工具(位於chrome://tracing)開啟。
要捕獲結果,請在 test_driver 資料夾中建立一個名為 perf_driver.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 目錄中會包含兩個檔案:
-
scrolling_summary.timeline_summary.json包含摘要。使用任何文字編輯器開啟該檔案以檢視其中包含的資訊。透過更高階的設定,你可以在每次測試執行時儲存摘要並建立結果圖表。 -
scrolling_timeline.timeline.json包含完整的時間線資料。使用 Chrome 瀏覽器的跟蹤工具(位於chrome://tracing)開啟該檔案。這些跟蹤工具提供了一個方便的介面來檢查時間線資料,從而發現效能問題的根源。
摘要示例
#{
"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
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
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,
);
}
},
);
}