01-test-strategy.md
目標讀者:AI 代理(規範解析)與人類 QA/開發人員。
背景:本文檔定義了認證考試題庫應用程式的測試策略。例如,在一個網路考試應用程式中,這些策略可確保題目、網路邏輯(如 IP 位址計算)及使用者進度的可靠性。
本專案遵循標準測試金字塔,以平衡執行速度與信心水準:
flutter_test:內建的單元與元件測試框架。mockito / mocktail:用於產生模擬類別 (Mock) 及模擬依賴項。flutter_gherkin:用於行為驅動開發 (BDD) 測試執行與定義 Feature 檔案。integration_test:官方的端到端 (E2E) 測試套件。golden_toolkit:(選用)用於跨不同螢幕尺寸的像素級截圖測試。RepositoryFactory.setMockRepository() 在元件與控制器測試中注入模擬資料來源。我們的目標程式碼覆蓋率指標如下:
flutter analyze lib 與 flutter test(單元/元件測試)。graph TD
A["提交程式碼"] --> B["Pre-commit Hooks"]
B -->|通過| C["推送到 GitHub"]
C --> D["GitHub Actions CI"]
D --> E["Linter 與格式化"]
D --> F["單元與元件測試"]
D --> G["整合測試 (Firebase 模擬器)"]
F --> H["覆蓋率報告"]
H --> I{"達到目標?"}
I -->|是| J["允許合併"]
I -->|否| K["阻擋 PR"]
02-unit-tests.md
目標讀者:AI 代理與 QA 工程師。
背景:核心通用認證考試模組的全面單元測試,涵蓋封包邏輯與通用題型。
| 測試 ID | 描述 | 輸入 | 預期輸出 | 邊界案例 |
|---|---|---|---|---|
MDL-001 |
Question.fromMap: 單選題解析 | Map 帶有 type: SINGLE_CHOICE |
Question 物件,正確解析單一答案 | 缺少選項陣列 |
MDL-002 |
Question.fromMap: 多選題 | Map 帶有 correctAnswer: [0, 2] |
正確解析 List<int> |
correctAnswer 列表為空 |
MDL-003 |
Question.fromMap: 拖放題 | Map 包含 dragOptions 與 dropTargets |
陣列對應正確 | 選項與目標數量不符 |
MDL-004 |
Question.fromMap: 模擬題 | Map 包含 practicalSolution |
解析出文字指令 | solution 欄位為 null |
MDL-005 |
Question.fromMap: 缺少可選欄位 | Map 省略 images, videoUrl |
物件解析為 null | 整個 Map 為空 |
MDL-006 |
Question.toMap: 序列化來回測試 | 有效的 Question 物件 | 完全相等的 Map | 所有可選欄位皆為 null 的物件 |
MDL-007 |
Question.copyWith: 部分欄位更新 | question.copyWith(title: '新標題') |
建立標題更新後的新實體 | 未傳入任何參數的 copy |
MDL-008 |
QuestionType 字串解析 | 'MULTIPLE_CHOICE' |
QuestionType.MULTIPLE_CHOICE |
未知的字串退回預設值 |
MDL-009 |
AppUser 角色字串解析 | 'admin' |
UserRole.admin |
無效的角色字串 |
MDL-010 |
AppUser.email getter | 透過 Firebase user 初始化的 User | 取得正確電子郵件字串 | Firebase user 無電子郵件 |
MDL-011 |
UserRole 列舉排序 | UserRole.admin > UserRole.guest |
評估為 true | 相同角色的比較 |
MDL-012 |
TransactionRecord.fromMap | Map 的 timestamp 為毫秒 |
正確的 DateTime 物件 | 負數時間戳 |
MDL-013 |
TransactionRecord.toMap | Transaction 物件 | 轉換為帶有毫秒時間戳的 Map | |
MDL-014 |
Comment.copyWith | comment.copyWith(likes: 5) |
更新後的評論 | |
MDL-015 |
ExamMetadata 建構子 | 有效的參數 | 不可變的 ExamMetadata 物件 |
| 測試 ID | 描述 | 輸入 | 預期輸出 | 邊界案例 |
|---|---|---|---|---|
SRV-001 |
SubnetValidator.ipToInt: 有效的 IPv4 | '192.168.1.1' (IP 位址) |
3232235777 |
|
SRV-002 |
SubnetValidator.ipToInt: 無效格式 | '999.999.999.999' |
null |
包含空白的字串 |
SRV-003 |
SubnetValidator.intToIp: 邊界值 | 0 |
'0.0.0.0' |
超過最大 32 位元整數 |
SRV-004 |
WrongQuestionsService.add: 新增 | 新的題目 ID | 加入清單,計數 = 1 | ID 已存在 |
SRV-005 |
WrongQuestionsService.add: 現有 | 現有的 ID | 計數遞增 | 最大整數限制 |
SRV-006 |
WrongQuestionsService.remove: 移除 | 現有的 ID | 從清單中移除 | ID 不存在 |
SRV-007 |
WrongQuestionsService.remove: 清理空值 | 考試的最後一個 ID | 刪除考試鍵值 | |
SRV-008 |
WrongQuestionsService.getKeys | 錯題鍵值 Map | 依時間戳降序排序 | 相同時間戳 |
SRV-009 |
WrongQuestionsService.getTodayCount | 換日開始 | 0 |
時區變更 |
SRV-010 |
WrongQuestionsService.incrementToday | increment() |
計數 + 1 | |
SRV-011 |
WrongQuestionsService.getGlobalCount | 多個考試鍵值 | 所有錯題總和 | 空資料庫 |
SRV-012 |
TimeService.validateTime: 有效 | NTP 時間與本機相符 | true |
NTP 伺服器逾時 |
SRV-013 |
TimeService.validateTime: 小幅回滾 | 時間回滾 < 1 小時 | true |
|
SRV-014 |
TimeService.validateTime: 大幅回滾 | 時間回滾 > 1 天 | false |
時區切換 |
SRV-015 |
TtsService.speakAwait: 依序播放 | 3 段文字 | 按順序播放 | |
SRV-016 |
TtsService.speakAwait: 逾時 | 長文播放 > 30 秒 | 觸發逾時例外 | |
SRV-017 |
ApiKeyService: 來回加密 | 加密字串 | 原始字串 | 格式錯誤的負載 |
SRV-018 |
DeviceSecurityService: Web 平台 | 執行於 Web | 傳回安全 | |
SRV-019 |
DeviceSecurityService: 偵錯模式 | 執行於 Debug | 傳回安全 | |
SRV-020 |
AdkClientService (with GemmaLocalService offline fallback): auto 模式雲端 | 雲端連線正常 | 使用雲端 API | |
SRV-021 |
AdkClientService (with GemmaLocalService offline fallback): auto 模式降級 | 網路連線錯誤 | 使用本機模型 | 未下載本機模型 |
SRV-022 |
NetworkService: 連線偵測 | Ping 成功 | true |
網頁驗證入口 |
| 測試 ID | 描述 | 輸入 | 預期輸出 | 邊界案例 |
|---|---|---|---|---|
CTL-001 |
ExamController: admin 讀取 | Admin 角色 | 傳回所有題目 | |
CTL-002 |
ExamController: guest 讀取 | Guest 角色 | 傳回 10 題已審核題目 | 題庫總數少於 10 題 |
CTL-003 |
ExamController: pending 讀取 | Pending 角色 | 傳回所有已審核題目 | |
CTL-004 |
ExamController: Repository 路由 | 切換至 MongoDB | 狀態正確更新 | 無效的資料庫類型 |
CTL-005 |
ExamController: 廣告解鎖到期 | 檢查解鎖狀態 | 24 小時後傳回 false |
|
CTL-006 |
MockExamController: 隨機選題 | 100 題的題庫 | 隨機抽出 50 題的子集 | 題庫總數不足 |
CTL-007 |
MockExamController: 計時器 | 120 分鐘 | 每秒遞減 | 應用程式進入背景 |
CTL-008 |
MockExamController: 分數計算 | 答對 40/50 題 | 分數 80.0 |
答題數為 0 |
CTL-009 |
MockExamController: 自動交卷 | 計時器歸 0 | 自動提交測驗 |
| 測試 ID | 描述 | 輸入 | 預期輸出 | 邊界案例 |
|---|---|---|---|---|
REP-001 |
RepositoryFactory: Mock 路由 | 設定 Mock 環境 | 傳回 MockExamRepo | |
REP-002 |
RepositoryFactory: powersync | 設定 SQLite 提供者 | 傳回 PowerSyncRepo | |
REP-003 |
RepositoryFactory: mongodb | 設定 MongoDB 提供者 | 傳回 MongoDbRepo | |
REP-004 |
RepositoryFactory: supabase | 設定 Supabase 提供者 | 傳回 SupabaseRepo | |
REP-005 |
RepositoryFactory: 預設值 | 未設定提供者 | 傳回 FirebaseRepo | |
REP-006 |
RepositoryFactory: 切換 | 呼叫 updateDefaultProvider |
後續呼叫傳回新提供者 | |
REP-007 |
ExamRepository 介面 | 介面檢查 | 所有方法皆可實作 |
03-widget-tests.md
目標讀者:AI 代理與 QA 工程師。
背景:確保練習測驗應用程式中的各個 UI 元件能正確渲染並符合預期行為。
| 測試 ID | 描述 | 預期渲染 | 預期互動 |
|---|---|---|---|
WID-001 |
QuestionOptionsWidget: 渲染單選題 | 視覺上顯示 A-D 選項。 | 無 |
WID-002 |
QuestionOptionsWidget: 點擊選項 | 被選取的選項顯示高亮。 | 點擊觸發 onSelect 回呼函式。 |
WID-003 |
QuestionOptionsWidget: 多選題 | 顯示核取方塊 (Checkbox) 而非單選按鈕。 | 點擊允許選取多個項目。 |
WID-004 |
QuestionOptionsWidget: 提交後高亮 | 正確答案顯示綠色,錯誤顯示紅色。 | 無 |
WID-005 |
QuestionOptionsWidget: 停用狀態 | 不透明度降低,視覺上呈現非作用中。 | 點擊沒有反應。 |
WID-006 |
QuestionView: 文字題目 | Markdown 文字正確渲染。 | 無 |
WID-007 |
QuestionView: base64 圖片 | 圖片成功解碼並顯示。 | 點擊圖片展開全螢幕。 |
WID-008 |
QuestionView: 拖放題 | 渲染 Draggable 與 DragTarget 區域。 | 項目可拖放並吸附至目標。 |
WID-009 |
ExplanationView: markdown 解析 | 粗體、清單、程式碼區塊正確渲染。 | 無 |
WID-010 |
ExplanationView: 語言頁籤 | 顯示 EN/zh-TW 頁籤。 | 點擊切換顯示的詳解語言。 |
WID-011 |
AiTutorPanel: 聊天介面 | 渲染輸入框與訊息清單。 | 無 |
WID-012 |
AiTutorPanel: 傳送訊息 | 出現使用者對話泡泡。 | 提交按鈕觸發 AI 串流回應。 |
WID-013 |
AiTutorPanel: AI markdown 回應 | AI 泡泡正確解析程式碼區塊。 | 無 |
WID-014 |
BannerAdWidget: 載入成功 | 廣告容器顯示內容。 | 無 |
WID-015 |
BannerAdWidget: 載入錯誤 | 元件自動摺疊或顯示佔位符號。 | 無 |
WID-016 |
SecurityWatermark: 渲染浮水印資訊 | 對角線顯示使用者 ID/Email。 | 無 |
WID-017 |
SecurityWatermark: 依角色調整不透明度 | 對未受信任的角色顯示較高不透明度。 | 無 |
WID-018 |
DiscussionWidget: 評論清單 | 渲染大頭貼、名稱、時間戳記。 | 無 |
WID-019 |
DiscussionWidget: 新增評論 | 顯示輸入框與傳送按鈕。 | 點擊傳送將新評論加入頂部。 |
WID-020 |
GlobalErrorBoundary: 錯誤捕獲 | 顯示通用錯誤畫面。 | 「重試」按鈕觸發回呼函式。 |