iT邦幫忙

2026 iThome 鐵人賽

DAY 27
1
Software Development

從脆弱腳本到可信任測試平台:自動化測試架構30天系列 第 27

Day 27|自動化測試技術債如何形成?7 大常見的防禦性坑洞與解法

  • 分享至 

  • xImage
  •  

「當團隊花在修復脆弱腳本、排查假警報與維護重複程式碼的時間,超過了編寫新測試與手動探索的時間,你的測試框架就已經落入了『技術債破產』的邊緣。」

大家好,我是 Jane,一個每天在第一線處理跨平台(Web & App)自動化測試、跟 CI/CD Pipeline 與各種歷史技術債奮戰的自動化測試工程師(SDET)。

在上集 Day 26 中,我們建立了嚴密的「產品 Bug vs. 測試 Bug」診斷決策樹,學會了如何用證據鏈劃分責任。

但當我們在團隊裡待得夠久,或是接手了一套「前人留下來的測試框架」時,最常面臨的夢魘往往是:

  • 「這套測試程式碼寫得像天書一樣,裡面堆滿了幾千行的重複代碼、硬編碼(Hardcode)與 time.sleep,沒有人敢改它,因為一改整個 CI 就全黑了!」

這種現象,就是著名的 「自動化測試技術債(Test Automation Technical Debt)」

今天這篇文章,我們就來好好拆解:自動化測試技術債是如何形成的?有哪些最常見的『防禦性坑洞』?以及第一線 SDET 該如何漸進式清債!

一、 技術債是怎麼產生的?7 大防禦性坑洞(Code Smells)

測試技術債很少是一夜之間爆發的,它通常是由無數個「為了趕時程而採取的快捷方式(Shortcuts)」累積而成:

─                    ┌───────────────────────────────┐
                     │         測試技術債 7 大成因      │
                     └────────────────┬──────────────┘
                                      │
     ┌────────────────────────────────┼──────────────────────────────┐
     ▼                                ▼                              ▼
【架構與撰寫習慣】                【維護與流程忽視】                【缺乏團隊契約】
1. 為趕時程複製貼上           4. 舊案例從不清理與刪除         6. 沒有 Code Review 機制
2. 任何邏輯都全走 UI 測試     5. 忽視警告與 Try-Catch 掩蓋    7. 只有原作者看得懂
3. 神奇數字與寫死帳號

1. 為趕發版時程複製貼上(Copy-Paste Driven Development)

為了快速交付,將「登入、設定 Header、建立資料」的 30 行程式碼複製貼用到每個測試檔案中。當 API 或 DOM 欄位修改時,需要手動修改 50 個地方。

2. 任何流程都寫成 UI 自動化(Over-reliance on UI)

不願意寫 API 測試或 Unit 測試,把所有的商業邏輯、邊界情境與欄位驗證全部寫成昂貴又脆弱的 Web / App UI 腳本。

3. 神奇數字與硬編碼(Magic Values & Hardcoding)

程式碼充斥著 time.sleep(5)locator("div.btn-v2") 或寫死的帳號密碼。接手的人完全不知道背後的商業意圖。

4. 舊案例從不刪除或重構(Zombie Tests)

產品需求早在半年前就改版了,但舊功能的測試案例依然留在專案裡,只是被打上 @pytest.mark.skip 或是被註解掉,演變成程式碼垃圾堆(Zombie Code)。

5. 濫用 try-catch 吞掉 Exception(Silent Failures)

為了讓 CI 報告呈現「全綠過關」,寫了危險的 try...except 吞掉錯誤,或是把斷言條件放寬到只驗 200 OK,打造虛假的安全感。

6. 測試程式碼完全沒有 Code Review 機制

團隊對產品 Code 嚴格要求 CR,但對測試 Code 卻放任自流。QA 寫完直接 Merge,各種 Bad Smells 無人把關。

7. 上帝物件與單一擁有者(God Objects & Bus Factor)

整個測試框架只有原作者一個人看得懂。檔案長達 3000 行,充滿了複雜的動態 Metaprogramming 或自創語法,一旦原作者離職,框架立刻癱瘓。

二、 Python / pytest 實戰:技術債程式碼與重構對照

我們來看一段典型帶有高額技術債的 pytest 測試,以及如何重構成乾淨的架構:

❌ 充滿技術債的原始程式碼(高維護成本):

Python

# tests/test_legacy_checkout.py
import time
import pytest
from playwright.sync_api import Page

# 技術債 1: 全域靜態變數與寫死帳號,極易引發併發衝突
SHARED_USER = "test_vip_001"

def test_checkout_legacy(page: Page):
    # 技術債 2: 硬編碼網址與無意義的 time.sleep
    page.goto("https://staging.example.com/login")
    time.sleep(2)

    # 技術債 3: 脆弱的 CSS 定位器與無語意操作
    page.locator("#input-usr").fill(SHARED_USER)
    page.locator("#input-pwd").fill("123456")
    page.locator("button.btn-login-v2").click()
    time.sleep(3)

    # 技術債 4: 全走 UI 去準備購物車資料 (耗時且脆弱)
    page.goto("https://staging.example.com/product/101")
    page.locator("button.add-cart").click()
    time.sleep(1)

    # 技術債 5: 吞掉 Exception 打造虛假的綠燈
    try:
        page.goto("https://staging.example.com/checkout")
        page.locator("#pay-btn").click()
        assert "success" in page.content()
    except Exception as e:
        print(f"失敗了但先過關: {e}")  # 致命!吞掉錯誤

⭕ 漸進式重構後的程式碼(低耦合、乾淨且穩定):

Python

# tests/test_checkout_clean.py
from playwright.sync_api import Page, expect
from pages.checkout_page import CheckoutPage  # 採用 Day 9/10 的 Page/Component 分層

def test_vip_checkout_should_succeed(page: Page, vip_user_factory, api_client):
    """
    測試意圖: 驗證 VIP 使用者能順利完成結帳流程
    重構重點:
    1. 採用動態獨立帳號 (vip_user_factory),解決資料污染
    2. 前置資料 (購物車) 改走 API 初始化,省去 10 秒 UI 操作
    3. 斷言明確,不使用 try-catch 吞錯誤
    """
    # 1. Arrange: 用 API 快速建好 VIP 帳號與購物車內容 (下沉至 API)
    vip_user = vip_user_factory()
    api_client.add_to_cart(user_token=vip_user["token"], product_id="PROD_101")

    # 2. Act: 使用 Playwright 進行精準 UI 結帳
    checkout_page = CheckoutPage(page)
    checkout_page.navigate_with_token(vip_user["token"])
    checkout_page.submit_payment()

    # 3. Assert: 採用 Web-first 斷言,內建自動重試
    expect(checkout_page.success_banner).to_be_visible()

三、 第一線 SDET 的「漸進式清債 4 步曲」

如果你的專案已經積攢了大量技術債,千萬不要嘗試「停工一個月把整個框架砍掉重寫」!這通常會以失敗告終,因為發版需求不會停下來等你。

建議採用以下 漸進式清債策略(Progressive Debt Reduction)

┌─────────────────────────────────────────────────────────────┐
│                 漸進式清債 4 步曲                             │
├─────────────────────────────────────────────────────────────┤
│ Step 1. 建立現況指標  ──▶ 算出 Flaky Rate、執行時間與失敗率       │
│ Step 2. 隔離致命病源  ──▶ 停用最常 Flaky 且維護成本高的 20% 案例   │
│ Step 3. 奠定底層基建  ──▶ 統一 Config, Fixtures 與 API Client  │
│ Step 4. 新舊童子軍法則──▶ 每次改動該模組時,順手重構 1 個舊案例     │
└─────────────────────────────────────────────────────────────┘
  1. Step 1:建立現況診斷指標(Metrics)
    用數據說話。統計專案的「Flaky 率(> 5%)」、「每日排查工時」與「執行總時間」。找出痛點最大的 Top 5 廢柴案例。
  2. Step 2:隔離與清理致命案例(Quarantine & Prune)
    將那些天天跳假紅燈、且商業價值極低(如:過期功能的 UI 測試)果斷移入隔離區或直接刪除。減少 noise 是重建團隊信任的第一步!
  3. Step 3:建立標準基礎設施(Base Infrastructure)
    抽離全域 conftest.py、統一的 API Client 工具與環境 .env 配置。讓寫新測試的人有好的範本可以遵循。
  4. Step 4:童子軍法則(Boy Scout Rule)
    「離開營地時,讓營地比你來時更乾淨。」每次為了新需求修改某個測試檔案時,順手將檔案裡的 time.sleep() 換掉,或是將寫死的值改為 Fixture。經過 2 個 Sprint,專案品質就會顯著提升。

明日預告

了解了自動化測試技術債的形成與重構策略後,下一個現實問題來了:

「面對一套已經沒人敢碰、滿是技術債的舊測試框架,我們該如何系統化地重構它,而不會破壞既有的品質防線?」

明天 Day 28,我們將來深度探討:《 Day 28|如何重構一套已經沒人敢碰的測試框架:實戰重構藍圖 》


上一篇
Day 26|如何判斷失敗是產品bug還是測試bug?排查順序與邏輯
下一篇
Day 28|如何重構一套已經沒人敢碰的測試框架:實戰重構藍圖
系列文
從脆弱腳本到可信任測試平台:自動化測試架構30天30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言