測試離線模式和網路切換:模擬器告訴你的不是真相

#行動應用 #測試策略 #工具

測試離線模式和網路切換:模擬器告訴你的不是真相 目錄 1. 那次只在地鐵才出現的 bug 2. 模擬器的網路模擬有多假 3. 實際的網路測試策略 4. 我們的 App 的離線測試清單 5. 結尾 那次只在地鐵才出現的 bug 根據 Opensignal 的《Mobile Network Experience》報告,全球行動用戶每天有相當比例的時間處於弱訊號或無訊號狀態——在亞洲的都市地鐵系統中,這個比例更高。對 App 開發者來說,離線和弱網不是邊緣情境,而是每個用戶每天都會遇到的真實場景。問題是,測試環境幾乎從來不模擬這個現實。 我們的 App 有一個功能:計時中途進入地鐵,網路斷掉,出站後恢復,計時繼續。 用戶回報:在地鐵裡斷網後,出站恢復網路,計時結束時種樹失敗,但 App 顯示「種植成功」。 我在模擬器上試了很多次:用 Network Link Conditioner 設定「100% 封包遺失」,過一段時間取消,繼續計時,種樹成功。 Staging 環境測試:完全正常。 最後用真機,在辦公室關閉 WiFi 切到行動網路又切回來,複現了。 根本原因:地鐵的斷網不是「完全無訊號」,而是「訊號很弱、封包遺失率高、延遲幾千毫秒、偶爾有連線」。這種「半連線」狀態,模擬器的「100% 封包遺失」根本模擬不到。在這種狀態下,種樹的 API request 成功發出,但 response 沒有回來,App 用 timeout 判斷為「成功」,但後端實際上沒有收到。 模擬器的網路模擬有多假 iOS Network Link Conditioner Xcode 的 Network Link Conditioner 提供幾個預設:3G、Edge、完全離線。這些是二元狀態——要嘛有,要嘛沒有。 真實的網路是連續的:30 Mbps 的穩定 WiFi、弱訊號的 LTE(5 Mbps + 150ms 延遲 + 5% 封包遺失)、地鐵進站瞬間(0.5 Mbps + 3000ms 延遲 + 80% 封包遺失)、完全斷線。 模擬器能模擬第一個和最後一個,中間那些它做不到。 Android 的狀況 Android 模擬器可以用 ADB 指令模擬網路延遲和封包遺失: 比 iOS 模擬器靈活一點,但還是無法模擬真實的「行動網路切換」的動態過程:進地鐵時的訊號從強到弱的漸變、切換基地台時的短暫中斷、WiFi 和行動網路之間的自動切換。 實際的網路測試策略 分層測試 第一層(模擬器 / 模擬工具):測試完全離線和完全連線的 happy/sad path。 第二層(真機 + 手動控制網路): 手動開關 WiFi、行動數據 把手機放進鐵盒(Faraday cage)模擬完全無訊號 快速切換 WiFi 和行動數據,測切換瞬間的行為 第三層(真實環境測試): 在實際的弱訊號環境(地下室、電梯)測試 長途通勤中測試(真實的網路切換) 第三層通常只在上線前做一次,不是每個 sprint 都跑。 Charles Proxy / mitmproxy 用 Proxy 工具可以做更精細的網路控制: 這讓你可以測「API 很慢但沒有 timeout 的情況下,App 的 loading state 是否正確」,比直接斷網更接近現實的弱網情境。 我們的 App 的離線測試清單 我把網路相關的測試分成三類: 必須在完全離線下 work 的功能 計時進行(不需要網路,本地計時) 查看已種植的森林(應該要有 cache) App 啟動(不應該因為無網路就白屏) 測試方法:切換到飛航模式,確認這些功能仍然可用。 網路恢復後應該自動同步的功能 離線計時完成 → 恢復網路後,硬幣和樹木自動更新 離線期間的操作紀錄 → 恢復後 sync 到後端 測試方法:飛航模式下操作,然後恢復網路,等待 30 秒,確認狀態同步。 弱網下的 UI 行為 Loading spinner 是否正確顯示 Timeout 的 error 訊息是否友善(不是只顯示錯誤代碼) Retry 機制:用戶點擊 retry 是否有效 測試方法:用 Charles Proxy 設定 5 秒延遲,然後逐漸增加到 30 秒(timeout 閾值)。 幾個常見的網路 bug 類型 1. 沒有 retry 邏輯 一次 API 失敗就顯示錯誤,不嘗試 retry。弱網環境下第一次請求很容易失敗,但第二次可能就成功了。 2. Race condition 在網路切換時 切換 WiFi 和行動數據的瞬間,有幾毫秒的「兩個都斷」的狀態。如果這個瞬間有 API 請求在進行,可能觸發 race condition。 測試方法:在 API 請求進行中快速切換網路(WiFi 關掉,行動數據打開),觀察 App 行為。 3. Cache 沒有失效機制 App 在離線時顯示 cache 的資料,這是對的。但用戶在 A 裝置操作後,在 B 裝置上看到的可能還是舊的 cache,因為 B 裝置的 cache 還沒有失效。 測試方法:在裝置 A 種樹,立刻切到裝置 B 查看森林,確認 B 有觸發 cache 失效並重新拉取資料。 結尾 網路相關的 bug 在測試環境很難重現,在用戶端的出現頻率遠比你想像的高——因為你的測試環境是辦公室穩定的 WiFi,用戶的環境是地鐵、電梯、偏遠地區、老舊的行動網路。 模擬器永遠不能完全替代真實環境的測試。但在有限的資源下,用 Proxy 工具模擬延遲和封包遺失、用真機手動切換網路、偶爾在真實的弱網環境測試——這三個層次組合起來,能覆蓋大部分網路相關的問題。 那個地鐵 bug 後來的修法是:計時完成的 request 改成帶 idempotency key,後端可以正確處理重複的請求,前端也加了重試機制。現在即使網路不穩,計時完成的結果還是能正確同步。 參考資料 Apple:使用 Network Link Conditioner 測試 — Xcode 內建的網路狀況模擬工具 Android 模擬器網路設定 — ADB 網路模擬指令與 Android Emulator 網路設定 mitmproxy 官方文件 — 強大的開源 HTTP/HTTPS Proxy,支援腳本化請求攔截 Charles Proxy 官方文件 — 主流的 HTTP Proxy 除錯工具 Opensignal Mobile Network Experience Report — 全球行動網路品質與連線狀況年度報告