這個案例的核心責任是說明 mock 如何讓「功能缺失」變得不可見。不同於 T.C1(功能存在但行為錯誤),這個案例是功能根本沒實作 — 因為 mock 不需要這個功能就能通過所有 test。

觀察

ttyd WebSocket 協議要求連線建立後發送一個 JSON frame 包含 base64 編碼的帳密({"AuthToken":"base64(user:pass)"}),ttyd 驗證通過後才開始推送 terminal output。app_tunnel 的 ConnectionManager 建立 WS 連線後直接開始監聽 stream,沒有發送 auth token。

指標
影響範圍連線建立後 ttyd 不推送資料(等 auth token),app 顯示空白終端機
Unit test 結果10 個 ConnectionManager test 全過(FakeWebSocketChannel.ready 立即完成)
Integration test 結果11 個 connection_flow_test 全過(同樣用 FakeWebSocketChannel
實機表現連線成功,終端機空白無輸出
修復新增 _sendAuthTokenIfNeeded()_establishWebSocket() 內呼叫

判讀

  1. Mock 的 happy path 比真實服務寬鬆FakeWebSocketChannelreadyFuture.value()(立即完成),stream 是開發者手動控制的 StreamController。真實 ttyd 的行為是:ready 完成代表 TCP+WS 握手成功,但 stream 要等 auth token 驗證後才有資料。Mock 把兩步合成一步。

  2. Integration test 名為整合實為 fakeconnection_flow_test.dart 標題是「端對端整合測試」,但內部使用 FakeWebSocketChannel + FakeBiometricService + InMemoryCredentialRepository — 三個核心依賴全是 fake。這個 test 驗證的是「假設所有外部服務都正常,內部狀態機是否正確」,不是「真實服務互動是否正確」。

  3. 功能缺失比功能錯誤更難被 test 抓到。功能錯誤(T.C1 text vs binary)至少有一個實作可以斷言;功能缺失意味著沒有程式碼可以 test。只有 protocol integration test(對真實服務跑)才能暴露「應該有但沒有」的行為。

策略

  1. Protocol integration test 必須涵蓋 auth handshake:連線 → 發送正確 auth token → 斷言收到 output;連線 → 不發送 auth token → 斷言 timeout 或斷線。
  2. 在企劃階段列出協議握手步驟:ttyd WS 協議的 auth handshake 應該在 spec 文件中明確列出,不依賴開發者記得實作。
  3. 區分「名義 integration」和「真實 integration」:test 名稱含 integration 但全用 fake,應標明 fake-integration 或改名 connection-state-machine-test

下一步路由