Day 10|從 UI 追到 API:教同事觀察 Network

前言

昨天是被動讀已經留下的證據,東西都在檔案裡,差別只在開哪一份。今天換一種姿勢:挑一個 UI 動作,主動追它到底做了什麼,然後面對昨天四種來源全部乾淨、卻確實有缺陷的那種情況。底下的數字都是 2026-08-02 實跑量到的,證據在 output/evidence/20260802-withbugs-network/,對照組在 output/evidence/20260802-toolshop-checkout/

為什麼要追到 network

UI 是最會騙人的一層。畫面上的每一個數字、每一個狀態,都可以是前端自己畫的。徽章從 0 變 1,可能是因為後端建了購物車,也可能只是前端把一個變數加一。這兩件事在畫面上長得一模一樣。

不追到 network,你驗的就是前端的渲染邏輯,不是產品的行為。而昨天那四種來源有個共同的盲點:它們都只回答「發生了什麼」,不回答「什麼沒發生」。截圖拍得到多出來的東西,拍不到少掉的請求;console 印得出報錯,印不出沉默;network 列得出送出去的每一筆,列不出本來該送卻沒送的那一筆。

缺席不會自己現身,所以今天要多帶一樣工具:基準線

受測站是 Toolshop,而且要用它的兩個版本:

版本 網址
乾淨版 https://practicesoftwaretesting.com/
刻意植入缺陷版 https://with-bugs.practicesoftwaretesting.com/

同一個產品、兩個環境,這件事本身就是方法。

實驗:按下 Add to cart,network 一個請求都沒有

登入 with-bugs 版,開商品頁,按 Add to cart 前後各導出一次 network:

playwright-cli network --raw > before-addcart.log
playwright-cli click e103            # Add to cart
playwright-cli network --raw > after-addcart.log
diff <(grep '=>' before-addcart.log) <(grep '=>' after-addcart.log)

結果:

請求數
按之前 2
按之後 2
差集

一個請求都沒有發出去。

但畫面說成功了:

導覽列的購物車徽章從 0 變 1,進到結帳第一步也看得到那一列商品。

沒有 toast 說失敗、沒有 console error、沒有非 2xx,昨天那四種來源全部乾淨。

那你怎麼知道少了什麼

比對乾淨版。同一個動作,同一天早上跑過:

[POST] https://api.practicesoftwaretesting.com/carts => [201] Created
[POST] https://api.practicesoftwaretesting.com/carts/01kz0rxps29tszvwpdaprxhggm => [200] OK

兩支請求。with-bugs 版是零支。

你得先知道「這個動作本來該發幾支請求」,才看得出來它沒發。而那兩支 POST /carts 是三個小時前留下的,當時看起來只是一筆無聊的紀錄。所以昨天最後那條結論要往前推一步:不只要留證,還要留正例。

這一輪還沒有系統性地建立這份基準,靠的是人記得早上跑過乾淨版。要變成流程,得有地方存它,那是第三週 charter 的事。

這比「回 500 被前端吞掉」更難抓

原本設想的場景是:送出後 UI 顯示成功 toast,但那支 POST 其實回 500,被前端吞掉了。

那種還算好抓,network 上有一筆紅的,掃一眼就看到。

這裡是什麼都沒有。掃 network 找紅色的人,會判它通過。

反過來說,這一輪也沒抓到「請求發了但回應被吞掉」的實例。with-bugs 這條是零請求,比較極端,真實產品更常見的是回了 4xx 或 5xx 但前端不顯示。

跑這一輪的指令長這樣:

/evidence-package 在 with-bugs 版登入後加一件商品進購物車,比對按鈕前後的 network 差異
/evidence-package 同一個動作在乾淨版跑一次,列出兩邊各發了哪些請求
/api-evidence 用已存在的 email 打 users/register,比對兩個版本的錯誤訊息

授權依據charters/toolshop-login-cart.yaml。不刪任何資料、不取得 admin token、不列舉他人資料、端點單發不連打。

對照組:同一張表自己跟自己打架

結帳第一步:

欄位 顯示
Price $14.15
Total(那一列) $00.00
Total(表尾) $14.15

這一筆不用比對基準線。同一張表裡,列小計是 0、表尾總計是 14.15,光看內部一致性就違規。

兩種缺陷放在一起,剛好是一組對照:

加入購物車沒發請求 小計 $00.00
怎麼發現 跟乾淨版比對請求清單 讀同一張表的兩個數字
需要基準線嗎 不要
只看畫面找得到嗎 找不到 找得到

$00.00 的成因今天沒有追。是前端算錯還是 API 回錯,要看那支請求的 response body,這一輪只記錄了現象。

端點側:兩筆用畫面永遠看不到的

一、註冊的錯誤訊息洩漏密碼提示。

curl -X POST https://api-with-bugs.practicesoftwaretesting.com/users/register \
  -H "Content-Type: application/json" \
  -d '{"email":"customer@practicesoftwaretesting.com", ...}'
{"email": ["User already registered - Your password hint is: Name of your cat!"]}

只要知道 email 就拿得到別人的密碼提示。乾淨版同樣的請求只回 "A customer with this email address already exists."

二、token 有效期差了五萬倍。

把登入拿到的 JWT 解開,算 exp - iat

版本 有效期
乾淨版 300 秒(5 分鐘)
with-bugs 15,600,000 秒(180.6 天

這兩筆的共同點:畫面上完全看不到。 密碼提示被前端過濾掉了才顯示,token 有效期根本不會顯示在任何地方。要看見它們,你得自己去打端點、自己去解 JWT。

有效期這條只驗了顧客帳號,其他角色是不是也一樣沒查。

順手撿到的

登入成功了,導覽列說查無使用者。這一筆不在今天的目標裡,先記下來。

  • 已登入狀態,導覽列顯示 User Data not found
  • GET /broken.png404
  • 文案:ContaktSerchReltded products
  • 結帳表格有兩欄都叫 Total

小結

挑一個 UI 動作,動作前後各導一次 network,先看差集;有差集就往下讀狀態碼與回應內容,差集是空的就換乾淨版跑同一個動作,拿它當尺量出少了哪幾支請求;畫面上根本不顯示的那些(密碼提示、token 有效期),要另外直接打端點。三條路走完,你手上才有「產品實際做了什麼」,而不是「畫面說它做了什麼」。

                        一個 UI 動作
                              │
                  按之前/按之後各導一次 network
                              │
                              ▼
                        diff 請求清單
                              │
                ┌─────────────┴─────────────┐
                ▼                           ▼
             有差集                      差集是空的
                │                           │
                ▼                           ▼
        讀狀態碼與回應內容            跟乾淨版比對請求清單
        (200 也可能是錯的)        (缺席只有靠基準線看得見)
                │                           │
                └─────────────┬─────────────┘
                              ▼
              畫面永遠不顯示的,直接打端點、自己解 JWT
                              ▼
                UI 說的 ≠ 產品做的,以後者為準

UI 說成功不代表有東西送出去,徽章變了、列表有了,可能全都是前端自己畫的。缺席也不會自己現身,看出「少發了一支請求」只能靠基準線,所以乾淨版不是拿來測的,是拿來當尺的。而掃紅色抓不到這一種:沒有 4xx、沒有 5xx、沒有 console error,什麼都沒有才是問題。

下一步

這兩天蒐到的東西夠多了:截圖、console、network、兩個環境的請求清單、端點側的原始回應。

但它們現在是散的。明天要處理交棒:一份證據包要湊到什麼程度,別人才不必重跑一次就看得懂你的結論。標準只有一條,每個結論都要有一份指得到的檔案接住它

---

參考資料

  1. Playwright — Network - 觀察、攔截、mock 請求
  2. Playwright API — Request/Response - 拿到 request 與 response 的細節
  3. Chrome DevTools — Network - 給讀者對照手動除錯的經驗
  4. MDN — HTTP response status codes - 判斷回應正不正常的第一層依據
  5. JSON Web Tokens - iatexp 的意義,本篇算有效期的依據
  6. OWASP — Session Management Cheat Sheet - 為什麼 token 有效期要短
  7. OWASP — Authentication Cheat Sheet - 為什麼錯誤訊息不該洩漏帳號是否存在或密碼提示
  8. testsmith-io/practice-software-testing - Toolshop 兩個版本的原始碼
  9. 本篇證據 output/evidence/20260802-withbugs-network/、對照組 output/evidence/20260802-toolshop-checkout/ - with-bugs 與乾淨版兩邊的請求清單原始檔