「當產品程式碼改動 1 行,你卻要跟著修改 10 檔測試程式碼時,問題往往不是出在產品改版,而是出在你的測試程式碼品質太差。」
大家好,我是 Jane。
在上集 Day 7 中,我們聊到了測試案例的粒度與獨立性,學會了如何把「巨無霸案例」拆解成小巧專一的測試。
今天我們要來面對一個許多 QA 甚至開發團隊常有的致命偏見:
這種心態,往往就是自動化專案走向死亡的開始。
在實務中,測試程式碼的生命週期跟產品程式碼一樣長,甚至因為它要頻繁應對產品需求的變動,它對「可讀性」與「可維護性」的要求比產品程式碼還要高! 如果測試程式碼寫得亂七八糟、到處都是複製貼上,半年後當你需要維護它時,你會發現自己根本看不懂當時在寫什麼,最後只能無奈地將案例 skip 掉。
今天這篇文章,我們就來盤點 7 個最常出現在自動化測試中的「程式碼壞氣味(Bad Smells)」,以及第一線 QA 該如何重構它們!
┌───────────────────────────────────────────────────────────────────┐
│ 測試程式碼常見壞氣味 │
├───────────────────────────────────────────────────────────────────┤
│ 1. 神奇等待時間 (Magic Sleep) 5. 神奇數字與字串 (Magic Values) │
│ 2. 複製貼上牆 (Copy-Paste) 6. 隱藏副作用 (Side Effects) │
│ 3. 測試邏輯洩漏 (Over-abstraction) 7. 幽靈斷言 (Weak/No Assertion) │
│ 4. 冗長混亂的命名 (Bad Naming) │
└───────────────────────────────────────────────────────────────────┘
Thread.sleep(5000) 或 await page.waitForTimeout(3000)。waitForElementVisible() 或 waitForResponse()。BeforeEach Hook、Fixture 或 Helper Utility,遵守 DRY 原則(Don't Repeat Yourself)。await page.fill('#input-3', 'test_user_881923') 或 expect(res.status).toBe(200)(但沒有解釋 881923 代表什麼特殊的權限帳號)。TestUsers.EXPIRED_VIP_USER)。checkEverything() 的黑盒子函式裡,失敗時根本不知道是哪裡出錯。page.locator('div > ul > li:nth-child(3) > button').click() 底層細節。test1()、checkLogin()、testError()。login_with_invalid_password_should_show_error_message()
uuid),並在 AfterEach 或 AfterAll 做好資源釋放與清理。expect(response).isNotNull(),卻沒有驗證 Response 裡的資料內容是否正確;或者連一個 assert 都沒寫,以為「程式碼沒噴 Exception 就算 Pass」。如果團隊裡 QA 寫的自動化程式碼從來不用經過 Dev 或其他 QA 的 Code Review,品質一定會失控。透過 CR,大家可以一起討論:
在產品程式碼中,我們可能會為了極致的效能而寫出一些複雜的演算法或設計模式;但在測試程式碼中,『可讀性』永遠是第一考量!
寫得直白、易懂、結構清晰,讓任何接手的人都能像讀「商業規格書」一樣閱讀你的測試程式碼,就是最高品質的體現。
當你發現某個頁面的定位器改版、或是某個 API 格式變動時,不要只是「修補」那個壞掉的腳本。順手把重複的邏輯抽離、把舊的 waitForTimeout 換掉。漸進式的重構,能防止測試程式碼快速腐爛。
說到了測試程式碼的抽離與分層,在 Web UI 自動化測試領域中,最知名且廣為人知的設計模式莫過於 Page Object Pattern (POM)。
然而,許多 QA 雖然用了 POM,卻寫出了幾千行的「肥大頁面類別(God Page Class)」,甚至把 Page Object 變成了另一種維護災難。
明天 Day 9,我們就來深度探討:《 Day 9|Page Object 到底該怎麼用?從濫用反思到 Component Object 實戰 》。