iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Claude AI

Claude × Playwright:從新進同事到 Agentic SDET 代理人系列 第 17

Day 17|別只測 Happy Path:帶它挑戰產品假設

  • 分享至 

  • xImage
  •  

前言

如何將證據收集下來這麼基本的事情,新同事馬上就上手了,為了讓它能夠有更多的成長,我決定直接給它一個目標,讓它自己去探索產品的正常路徑和不正常的路徑。

經過幾天的觀察,Happy Path 新同事已經已經駕輕就熟,接著,我們讓它學習如何走到非 Happy Path 以外的路徑。

為什麼要離開快樂路徑(Happy path )

快樂路徑(Happy path )顧名思義,會讓大家很快樂,因為它幾乎不會輕易失敗。使用者照順序走、每一步都填對、不重整不返回不開第二個分頁 —— 這條路是產品被設計出來的樣子,也是開發自己測過最多次的路。你沿著它跑一百輪,得到的是一百次「正常」。

問題是缺陷不住在那裡。真實使用者會按上一步、會連點兩下、會把分頁放到隔天再回來、會直接把網址貼給同事。這些行為在 happy path 上一條都不會發生,所以那條路上的一百次綠燈,證明不了它們。

那為什麼不乾脆隨便亂點?因為亂點沒有依據,你事後說不出自己測了什麼、還有哪裡沒測。「非 happy path」不是一個地方,是無限多個地方,沒有一張表幫你挑,二十五步很快就用完了,而且用在哪裡你也講不清楚。

所以今天要補的是那張表:把 happy path 底下那些沒說出口的假設一條條列出來,再告訴你每一條怎麼反過來測。

如何選擇

今天一個畫面可能會有很多地方可以點選,那我們要怎麼去選擇說應該要點選哪裡、哪邊的風險會比較大,會讓我們比較容易找到 Bug?

所以我們今天呢,要先介紹的是啟發式的規則。

Happy Path 不是一條路徑,是一串沒說出口的假設。使用者會照順序走、加了就能移除、畫面顯示成功就是真的成功,這些話沒有人寫在規格裡,但整個流程都建立在它們上面。啟發式規則做的事情,就是把這些假設一條條列出來,再告訴你怎麼把它反過來測。

這是 references/heuristics.md 裡的七條,中間那一欄才是重點:

heuristic 隱含假設 反過來怎麼測 購物車情境例子
逆操作 有新增就有還原 加了就移除、設了就清空、上一步/返回 加入後移除、數量歸零
非法狀態轉移 使用者照步驟順序走 跳過前置、直接打後面步驟的網址、返回已完成步驟 未登入直接進 /checkout
狀態組合 每種狀態畫面都正常 空/滿、登入/未登入、新/舊資料,看同一個畫面 空車時看 navbar,購物車圖示還在嗎
重複/併發 使用者只做一次 連點兩次、開兩個分頁、reload 到一半送出 連點兩次 Add to cart
權限越界 只有該看的人會看 未登入打需授權的資源、用別人的 id 未登入的頁面對 /users/me 的行為
過期 資料永遠新鮮 放久了再操作、用過期的 id 或 session 用很久以前的 cart id 去結帳
中斷/取消 使用者會把流程走完 中途離開、reload、關頁再回來 填到一半 reload 結帳頁

拿「加入購物車」這一個動作來看,Happy Path 只走一次:按下 Add to cart,數字變成 1,結束。但同一個按鈕,七條表各自會問出不同的問題。逆操作問的是加了之後移不移得掉、數量能不能歸零;重複問的是連點兩次會變成 2 還是變成 1;狀態組合問的是購物車空掉之後,那個圖示和金額區塊長什麼樣子;中斷問的是把商品加進去、關掉分頁、隔天再開,車裡的東西還在不在,價格是不是還是舊的。

一個按鈕,七個角度,這就是「往哪裡歪」的答案。

為什麼是這七條,不是三條或三十條。 這七條的共同點是不依賴產品知識。你不知道這家店賣什麼、免運門檻多少、會員等級怎麼算,照樣打得動它們。需要產品知識才問得出來的挑戰,例如折扣能不能疊加、庫存扣減的時機,那些屬於 knowledge/ 的範圍,是 Day 5 的事。這張表管的是「任何產品都成立」的那一層。

還有一條紀律要先講:每次挑戰之前,先點名打的是哪一個假設,寫進 log 的 assumption 欄。點不出名字的動作是亂點,不是探索。而挑戰出來的可疑現象一律先標成 anomaly,不當場定罪,判定要交給 test-oracle,那是明天的主題。

端點側:畫面幫不了你的那八條

除了 UI 的各式功能測試,其實我們在 API 還有專屬需要確認的點。在這份 heuristic test 的檔案中,也有描述如何測試 API。

上面那七條在端點上照樣成立,逆操作就是建了再刪、狀態組合就是空清單與滿清單、重複就是同一筆送兩次。但下面這幾條只有直接打端點才戳得到,因為畫面根本不會幫你送出這種請求:

heuristic 隱含假設 反過來怎麼測 例子
方法竄改 只有畫面用得到的方法會被呼叫 同一路徑換 PUT / PATCH / DELETE / OPTIONS 唯讀端點是不是也接受 DELETE
越權取用 只有該看的人會帶著 token 來 換另一個帳號的 token、換別人的 id、整個不帶憑證 GET /users/2 帶 user1 的 token
欄位增減 前端只會送畫面上有的欄位 少送必填、多送不該送的(roleis_adminprice 建立帳號時夾帶 role: admin
型別與邊界 送來的一定是合法值 字串換數字、負數、超長、null、巢狀物件換陣列 qty: -1qty: "abc"
契約以外的回應 回什麼就是什麼 拿回應對契約逐欄位比,看有沒有多回不該回的 使用者物件夾帶密碼雜湊
憑證生命週期 token 一直有效 用過期的、剛登出的、別的環境的 token 重放 登出後拿舊 token 再打一次
請求標頭 客戶端會送對的標頭 Content-Type、拿掉它、送不支援的 Accept 表單編碼冒充 JSON
併發與順序 請求會照順序到 同一筆同時送兩次、先刪再改、分頁途中改資料 同時扣同一份庫存

舉兩個例子把表格說清楚。

第一個是「欄位增減」。 註冊頁上只有姓名、信箱、密碼三格,所以前端送出去的 JSON 就是這三個欄位,這是畫面替你保證的事。但那個保證只存在於瀏覽器裡。你自己組一個請求,在 body 裡多塞一個 "role": "admin",後端如果是把整包 body 直接餵給 ORM,那個欄位就會被寫進資料庫。這種洞從畫面上是看不到的,因為沒有任何一個輸入框叫做 role,你點爛滑鼠也點不出來。同一條反過來還有另一半:把必填欄位整個不送,看它是回 422 還是直接存了一筆殘缺資料。

第二個是「越權取用」。 使用者資料的端點長得像 GET /users/{id},畫面永遠只會拿你自己的 id 去打它,所以「只有本人會看到本人的資料」這件事,同樣是畫面替你保證的。要測它,就用 A 帳號換來的 token 去打 B 的 id。正確的回應是 403 或 404;如果回了 200 而且帶著 B 的信箱和地址,這就是一條完整的越權。再補一次不帶憑證的請求,正確答案是 401,回 200 就代表這個端點根本沒有掛驗證。

這一條有個必須先講的界線:只驗存在性,命中就停手。驗到一次回 200 就記錄下來,不要接著把 id 從 1 數到 100 把別人的資料撈成一份清單。那已經不是測試了。

怎麼讓它照這張表去打

我們了解啟發式的這張表格之後,那我們要怎麼讓我們的新同事去使用這張表格呢?

第一件事是它不用你叫。references/heuristics.md 是 reference,不是 skill,explore 動手之前會自己去讀它。所以昨天那句指令照樣可以用,只是這一次可以在後面補一句方向:

用 charters/toolshop-checkout-payment.yaml 跑一輪探索,這輪專打非法狀態轉移和權限越界

這樣講的好處是你點名了假設,它就會照著那兩條去翻,而不是七條各摸一下。

如果你想讓這個方向固定下來、每次跑都生效,就寫進 charter 的 tours 欄位。tour 是鏡頭,heuristic 是到了那裡要戳哪個假設,兩個搭在一起用:

charter:
  goal: "在 Bug Hunting build 獵一輪 bug,含註冊/付款/API/安全類(非破壞性)"
  tours: [authz]

tours: [authz] 的意思是「戴上權限這副眼鏡,把同一個資源分別用本人、別人、無憑證各打一次」。鏡頭清單在 references/tours.md,一共八個,一次挑兩三個最相關的就好,全跑只會把步數用完。

API 那張表則有個開關:charter 要有 endpoints,它才會讀。沒有端點清單的 charter,它就只在畫面側翻假設。想讓它打端點,清單要寫給它:

  api_base: "https://api-with-bugs.practicesoftwaretesting.com"
  endpoints:
    - "POST /users/login"
    - "GET /users/me"
    - "GET /users/{id}"
  auth_context:
    as: "顧客帳號換來的 bearer token"
    other: "探索時自行註冊的第二個測試帳號,用來驗越權"

auth_context.other 這一欄是越權那條的前提。沒有第二組憑證,它連比都沒得比。

最後一件事:挑戰的自由度是靠邊界換來的,不是靠膽子。charter 的 out_of_boundsauthorization 兩欄要在跑之前寫死,例如「SQLi 只驗能不能繞過登入,命中就停手」「不得寫迴圈連打端點」「不得刪除任何既有資料」。這些寫清楚了,你才敢放手讓它去歪。

實驗:未登入直接打 /checkout,回 200

在未登入網站的情況下,我們的購物車裡可能已經加入了商品。這時候,只要按下「下一步」,其實你會發現有一些 UI 會幫忙顯示需要登入的訊息。

未登入按下結帳,跳出要求登入的 modal

照按鈕走的人看到的就是這個。看起來很像一道門。

今天如果我們使用 API,直接把網址打進去,它可能會回傳 200,或者是回傳一些 Payment、Credit Card 之類的訊息。

未登入直接輸入 /checkout 網址,頁面正常顯示 Address Details 與 Place Order

不按那顆按鈕、直接把網址打進去:/checkout 回 200,沒有重導,Address Details 與 Place Order 都在。

未登入直接輸入 /payment 網址,頁面正常顯示 Payment 與 Name on Card

/payment 一樣。那個 modal 從來不在後端,它只在前端的那顆按鈕上。

網址 未登入的回應 頁面內容
/checkout 200,沒有重導 Address Details、Review Your Order、Place Order
/payment 200,沒有重導 Payment、Name on Card

這個例子說明了我們不能只確定 UI 上是不是正常的,有時候需要透過 API 去模擬一些非使用者會走的路徑,這樣我們才能確保系統在非正常使用的時候,是不是也符合預期的行為。

回頭把這個案例掛回表上:它打的是非法狀態轉移加上權限越界兩條。那個要求登入的 modal 不是守衛,是一張貼在門上的紙。只走 Happy Path 的人永遠看不到這件事,因為他會乖乖照著按鈕走,而「按鈕會擋住我」正是一個沒說出口的假設。

小結

Happy path 是一串沒說出口的假設,啟發式那兩張表把假設一條條攤開,再告訴你怎麼反過來測;畫面側七條不依賴產品知識,端點側八條要 charter 填了 endpointsauth_context 才打得動。挑戰之前先點名打的是哪一個假設,挑戰出來的東西一律先標 anomaly 不當場定罪。而放手讓它去歪的前提是 out_of_boundsauthorization 已經寫死 —— 自由度是靠邊界換來的,不是靠膽子。

              Happy Path(幾乎不會紅)
                        │
                        ▼
            底下壓著一串沒說出口的假設
        照順序走|加了能移除|顯示成功就是成功
        按鈕會擋我|只有本人看得到自己的資料
                        │
        ┌───────────────┴───────────────┐
        ▼                               ▼
   畫面側七條                       端點側八條
 逆操作|非法狀態轉移               方法竄改|越權取用
 狀態組合|重複併發                 欄位增減|型別邊界
 權限越界|過期|中斷               契約外回應|憑證生命週期
                                    請求標頭|併發順序
   不需產品知識                     需要 charter 填
                                  endpoints + auth_context
        └───────────────┬───────────────┘
                        ▼
              動手前先點名 assumption
              「我現在打的是哪一個假設」
                        │
                        ▼
              out_of_bounds / authorization
              先寫死(命中就停手、不連打、不刪資料)
                        │
                        ▼
              可疑現象一律標 anomaly
              定罪不在今天

happy path 上的一百次綠燈證明不了任何非正常路徑,因為那些行為在那條路上一次都不會發生。但離開 happy path 不等於亂點,點不出名字的動作是亂點,先寫下 assumption 才叫探索。而畫面上的守衛可能只是一張貼在門上的紙,/checkout 未登入回 200 這種事,只有繞過畫面直接打才看得到。

下一步

今天挑戰出來的東西全都標成 anomaly:看到了、覺得怪,但沒有定罪。

明天處理定罪這件事。你憑什麼說這是錯的? 找不到那個判準,你只能說「怪」,不能說「錯」 —— 而那個判準有名字,叫 test oracle。


參考資料

  1. James Bach — Heuristic Test Strategy Model - HTSM/SFDPOT 的原始出處,這兩張表的祖宗
  2. James Bach — Exploratory Testing - Rapid Software Testing 對 heuristic 的定義
  3. Elisabeth Hendrickson — Explore It! - 第二節那張表的思路來源
  4. Toolshop(Practice Software Testing) - 第三節三筆的受測站
  5. Automation Exercise - Day 9 那個案例的受測站
  6. 本專案 references/heuristics.md - 兩張表的真檔
  7. 本專案 references/tours.md - 八個鏡頭

上一篇
Day 16|教它自己選下一步,而且不鬼打牆:Action Selection 與路徑記憶
下一篇
Day 18|這到底是不是 Bug?教它建立 Test Oracle
系列文
Claude × Playwright:從新進同事到 Agentic SDET 代理人21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言