skills/test-async-guardian/SKILL.md
Flutter/Dart 測試異步資源管理守護者。用於:(1) 診斷測試卡住問題,(2) 審查測試程式碼中的異步資源清理,(3) 提供 tearDown 最佳實踐,(4) 掃描潛在的資源洩漏風險。觸發場景:測試卡住、撰寫新測試、Code Review 測試程式碼、執行 flutter test 前自動掃描。
npx skillsauth add tarrragon/claude test-async-guardianInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
測試異步資源管理守護者 - 防止測試因未清理的異步資源而卡住。
Dart/Flutter 測試框架會等待所有未完成的 Future 才結束測試。如果測試啟動了長延遲的異步操作但沒有正確清理,測試會無限期卡住。
| 資源類型 | 問題描述 | 清理方法 |
|---------|---------|---------|
| 未完成的 Future | 長延遲異步操作沒有等待完成或取消 | service.clearAllQueries() 或 service.dispose() |
| Timer.periodic | 週期性定時器未取消 | timer.cancel() |
| StreamController | 廣播流未關閉 | controller.close() |
| Completer | Completer 未完成 | completer.complete() |
| 網路依賴 | flutter test 啟動時需要 pub.dev DNS 解析 | 確保網路連線,或使用離線模式 |
| testWidgets 時間 | Future.delayed 在 widget 測試中無法自動推進 | 呼叫 tester.pump() 或改用 test() |
| MCP run_tests 大量輸出 | 全部測試輸出量過大導致 MCP 卡住 | 使用 paths 參數或改用 flutter test |
late BookQueryService bookQueryService;
setUp(() {
bookQueryService = BookQueryService(apiService: mockApiService);
});
tearDown(() {
bookQueryService.clearAllQueries(); // 清理未完成的查詢
});
late MockSearchBookViewModelForBatch mockSearchViewModel;
tearDown(() {
mockSearchViewModel.setSlowSearch(false); // 重置慢速模式
mockBookRepository.setSlowQuery(false);
mockBatchEnrichBooksUseCase.setFastExecution(true);
container.dispose();
});
final StreamController<EnrichmentProgress> _progressController =
StreamController<EnrichmentProgress>.broadcast();
void dispose() {
_progressController.close();
}
Timer? _memoryMonitorTimer;
void startMonitoring() {
_memoryMonitorTimer = Timer.periodic(interval, (_) {
captureSnapshot();
});
}
void dispose() {
_memoryMonitorTimer?.cancel();
_memoryMonitorTimer = null;
}
// [FAIL] 危險:10 秒延遲,測試只等 10ms
when(mockApiService.queryByIsbn('xxx'))
.thenAnswer((_) async {
await Future.delayed(const Duration(seconds: 10));
return data;
});
await Future.delayed(const Duration(milliseconds: 10));
// 測試結束,但 Future 還在運行!
修復:添加 tearDown 清理或縮短延遲時間。
// [FAIL] 危險:Timer.periodic 未在 tearDown 中取消
Timer.periodic(Duration(seconds: 1), (timer) {
captureSnapshot();
});
修復:保存 Timer 引用並在 dispose/tearDown 中取消。
// [FAIL] 危險:broadcast StreamController 未關閉
final controller = StreamController<Event>.broadcast();
// 測試結束但 controller 仍在監聽
修復:在 dispose 方法中調用 controller.close()。
# [FAIL] 危險:網路斷線時 flutter test 卡在 Resolving dependencies
Resolving dependencies...
ClientException with SocketException: Failed host lookup: 'pub.dev'
症狀:
修復:確認網路連線後重新執行測試。
// [FAIL] 危險:testWidgets 使用虛擬時鐘,Future.delayed 永不完成
testWidgets('事件測試', (tester) async {
await Future.delayed(Duration(milliseconds: 50)); // 永遠等待!
expect(events.length, 3);
});
症狀:
修復選項:
// 選項 A: 添加 pump(需要 Widget 環境時)
testWidgets('事件測試', (tester) async {
await Future.delayed(Duration(milliseconds: 50));
await tester.pump(Duration(milliseconds: 50)); // 推進虛擬時間
expect(events.length, 3);
});
// 選項 B: 改用 test(推薦,純邏輯測試時)
test('事件測試', () async {
await Future.delayed(Duration(milliseconds: 50)); // 正常執行
expect(events.length, 3);
});
判斷標準:
# [FAIL] 危險:不指定 paths 執行全部測試會卡住 20+ 分鐘
mcp__dart__run_tests (無 paths 參數)
症狀:
flutter test 直接執行約 85 秒完成根本原因:
修復:
# [OK] 正確 - 必須指定 paths 參數限制測試範圍
mcp__dart__run_tests(roots: [{"root": "file:///path", "paths": ["test/domains/"]}])
mcp__dart__run_tests(roots: [{"root": "file:///path", "paths": ["test/unit/core/"]}])
# [OK] 推薦 - 全量測試使用 Bash(最穩定)
flutter test --reporter compact
./.claude/hooks/test-summary.sh
適用場景對照: | 測試範圍 | MCP run_tests | flutter test | |---------|---------------|--------------| | 單一檔案 | [OK] 適用 | [OK] 適用 | | 單一目錄 (paths) | [OK] 適用 | [OK] 適用 | | 全部測試 | [FAIL] 禁止 | [OK] 推薦 |
# 掃描單個測試檔案(嚴格模式)
uv run .claude/skills/test-async-guardian/scripts/async_resource_scanner.py \
test/unit/domains/scanner/book_query_service_test.dart
# 掃描整個測試目錄
uv run .claude/skills/test-async-guardian/scripts/async_resource_scanner.py \
test/unit/ --recursive
# 警告模式(不阻止執行)
uv run .claude/skills/test-async-guardian/scripts/async_resource_scanner.py \
test/unit/ --warn-only
腳本已整合為 PreToolUse Hook,在執行 flutter test 或 dart test 前自動觸發掃描。
當測試卡住時:
檢查是否使用 MCP run_tests 執行全部測試
# 如果使用 mcp__dart__run_tests 不指定 paths,會卡住 20+ 分鐘
# 解決方案:改用 flutter test 或指定 paths 參數
# [OK] 正確方式
flutter test --reporter compact
mcp__dart__run_tests(paths: ["test/domains/"])
檢查網路連線
# 確認網路連線正常(排除 pub.dev DNS 問題)
ping -c 1 pub.dev || echo "網路可能有問題"
識別卡住的測試
# 使用 timeout 限制測試時間(macOS 需要安裝 coreutils)
timeout 30 flutter test test/unit/path/to/test.dart
# 或使用背景程序方式
flutter test test/unit/path/to/test.dart & sleep 30 && pkill -f flutter_tester
掃描異步資源問題
uv run .claude/skills/test-async-guardian/scripts/async_resource_scanner.py \
test/unit/path/to/test.dart
檢查報告中的問題
應用修復建議
async_resource_scanner.py - 異步資源掃描腳本,支援嚴格模式和 Hook 模式async-cleanup-patterns.md - 清理模式參考和真實案例pre-test-scan.py - PreToolUse Hook 入口腳本Last Updated: 2026-03-02 Version: 1.0.0
development
Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
development
Claude Code release notes 框架影響評估工具。比對 last-reviewed 版本篩出新版本,逐項分類(對框架有幫助 / 需評估 / 無影響 / 不適用),對採用項引導建 ANA + WRAP + spawn 落地。Use when: 執行 /release-notes 看到新版本、定期檢查 CC 更新、評估新功能對專案框架的影響時。Triggers: release notes, release-notes, CC 更新, claude code 更新, 版本更新評估, 新功能評估, 框架影響評估。
development
Assertion design judgment framework for flaky and design-quality issues. Use when writing tests, reviewing assertions, diagnosing flaky tests, or deciding if a timing/float/cache assertion is appropriate. Do NOT use for API syntax or refactoring.
tools
Chrome Extension 實機測試與 debug 工作流,以 chrome-devtools-mcp 為核心工具。Use when: (1) 完成功能後實機驗證 / manual test / 試看看 / 跑看看 / verify feature, (2) extension debug / popup 不作動 / content script 不注入 / service worker 報錯 / background 出問題, (3) 安裝 unpacked extension / load unpacked / 載入未封裝, (4) 看 console / 看 network / 看 log / view console / inspect requests, (5) 功能更新後重新載入 extension / rebuild reload / reload extension。涵蓋 Manifest V3 service worker / content script / popup / options page 的 chrome-devtools-mcp 工具呼叫流程。不取代 Puppeteer / Playwright 自動化 E2E(CI 用),定位為開發期手動驗證與 LLM-assisted debug。