iT邦幫忙

2026 iThome 鐵人賽

DAY 16
1
Software Development

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

Day 16|測試資料為什麼比測試程式更難管理?剖析資料依賴與污染的陷阱

  • 分享至 

  • xImage
  •  

「寫出一套漂亮的測試程式碼只要一週;但維護一套乾淨、可重複執行的測試資料庫,卻要跟整個團隊的開發習慣與系統狀態纏鬥一整年。」

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

恭喜大家跟著我完成了第三部分關於 UI 抗脆性與 Flaky test 治理的討論!

從今天開始,我們要跨入自動化測試最硬核、也最容易被忽略的領域:《第四部分:測試資料、環境與 API 整合》

許多剛投入自動化的工程師常有一種錯覺:「只要把元素定位好、等待條件寫對,自動化測試就完成了。」
但到了實務中,不論是在 Web 端還是 App 端,導致 CI/CD 上測試爆紅燈的原因,常常根本不是測試程式碼壞掉,而是『測試資料(Test Data)』爛掉了!

今天這篇文章,我們就來好好聊聊:為什麼測試資料比測試程式碼更難管理?我們常踩到的資料陷阱有哪些?

一、 為什麼測試資料管理是 SDET 的終極考驗?

測試程式碼是「無狀態(Stateless)」的——每次執行時,程式碼邏輯都不會自己改變。
但測試資料與環境卻是「有狀態(Stateful)」的——每一次測試執行、每一個 API 呼叫,都會在資料庫或系統中留下痕跡。

在 Web 與 App 自動化測試中,測試資料管理主要面臨以下 5 大致命陷阱

                    ┌─────────────────────────┐
                    │  測試資料管理的 5 大陷阱  │
                    └────────────┬────────────┘
                                 │
     ┌───────────────────────────┼───────────────────────────┐
     ▼                           ▼                           ▼
1. 帳號與狀態被共用與污染       3. 特殊商業狀態無法重建       5. 資料與環境不一致 (Env Drift)
2. 案例間存在執行順序依賴       4. 測試後遺留垃圾資料未清理

陷阱 1:帳號與資料被共用/污染(Data Contamination)

  • 現象:Web 端與 App 端的測試案例共用了同一個預設測試帳號 test_user_01
  • 痛點:當 Web 端的測試案例執行了「修改個人資料/變更密碼」,隨後執行的 App 端測試案例就會因為密碼不符或狀態改變而全數失敗;在平行執行(Parallel Execution)時更會引發極難排查的 Race Condition。

2. 測試依賴執行順序(Order Dependency)

  • 現象test_02_checkout.py 假設資料庫裡已經有 test_01_add_to_cart.py 建立好的購物車資料。
  • 痛點:一旦 pytest 採用隨機順序或單獨執行 test_02_checkout.py 時,測試立刻崩潰。

3. 特殊商業狀態極難重建(Complex Initial State)

  • 現象:要測試「VIP 會員退貨流程」,該帳號必須具備「完成 5 筆消費、累積 1000 點紅利、且訂單處於已出貨 7 天內」的複雜狀態。
  • 痛點:如果只靠 UI 手動一步步去點出這個狀態,光是 Arrange 階段就要花 3 分鐘,且極易因為中間任何一步微小改變而失敗。

4. 測試後遺留垃圾資料(Data Pollution)

  • 現象:每次自動化測試都建立一個「測試商品_12345」,執行一個月後,Staging 環境的 DB 裡塞滿了幾萬筆髒資料,甚至導致搜尋與列表頁面載入超時(Timeout)。

5. 資料與環境不一致(Environment Drift)

  • 現象:測試資料在 Dev 環境跑得好好的,搬到 Staging 或 Pre-prod 環境就因為少了特定優惠券 ID 或商品類別而全部跳錯。

二、 Python / pytest 實戰:痛點程式碼對比

我們來看一段在實務中最典型、也最脆弱的資料處理寫法:

❌ 脆弱的資料寫法(共用固定帳號、依賴環境硬編碼):

Python

# tests/test_user_profile_brittle.py
from playwright.sync_api import Page, expect

def test_update_profile_brittle(page: Page):
    page.goto("https://staging.example.com/login")

    # 脆弱點 1: 硬編碼共用帳號!一旦別的案例修改了密碼,這個案例就死了
    page.get_by_test_id("username").fill("vip_user_01")
    page.get_by_test_id("password").fill("password123")
    page.get_by_test_id("login-btn").click()

    page.goto("https://staging.example.com/profile")

    # 脆弱點 2: 修改了全域共用帳號的狀態,導致後續案例拿到被污染的姓名
    page.get_by_test_id("nickname-input").fill("New_Nickname_Jane")
    page.get_by_test_id("save-btn").click()

    expect(page.get_by_test_id("toast-msg")).to_have_text("修改成功")

⭕ 穩定的資料寫法(利用 pytest Fixture 動態產生獨立資料與清理):

Python

# tests/conftest.py
import uuid
import pytest
from utils.api_client import APIClient  # 假設的 API 工具

@pytest.fixture(scope="function")
def random_user(api_client: APIClient):
    """
    動態產生獨立的測試使用者,測試結束後自動透過 API 清理
    """
    # 1. Arrange: 動態生成唯一識別碼,確保每個測試案例的資料完全隔離
    unique_id = str(uuid.uuid4())[:8]
    user_data = {
        "username": f"user_{unique_id}",
        "email": f"test_{unique_id}@example.com",
        "password": "SecurePassword123!"
    }

    # 直接透過 API 快速建立使用者狀態,不走昂貴的 UI 註冊流程
    user = api_client.create_user(user_data)

    # 將動態建立的 user 物件交付給 Test Case 使用
    yield user

    # 2. Teardown: 測試結束後清理資料,保持環境乾淨
    api_client.delete_user(user["id"])

Python

# tests/test_user_profile_stable.py
from playwright.sync_api import Page, expect

def test_update_profile_stable(page: Page, random_user: dict):
    """
    使用獨立的 random_user Fixture,完全不擔心與其他 Web/App 案例衝突
    """
    page.goto("https://staging.example.com/login")

    # 使用當次測試專用的獨立帳號
    page.get_by_test_id("username").fill(random_user["username"])
    page.get_by_test_id("password").fill(random_user["password"])
    page.get_by_test_id("login-btn").click()

    page.goto("https://staging.example.com/profile")
    page.get_by_test_id("nickname-input").fill("Updated_Nickname")
    page.get_by_test_id("save-btn").click()

    expect(page.get_by_test_id("toast-msg")).to_have_text("修改成功")

三、 第一線 SDET 的測試資料管理 3 大金律

身為同時把關 Web 與 App 品質的 SDET,在規劃測試資料時請務必守住這 3 個原則:

  1. 資料獨立性原則(Data Isolation)
    每個測試案例應該擁有自己獨立的測試資料。能動態生成的,就絕對不要硬編碼(Hardcode)共用!
  2. Arrange 階段盡量走 API / DB,不要走 UI
    準備測試前置資料時,透過 API 或直接存取 DB 建立狀態比走 UI 快 10~20 倍,且能大幅避開 UI 變動帶來的 Flaky 機率。
  3. 明確的資料生命週期管理(Setup & Cleanup)
    利用 pytest 的 Fixture 養成「誰建立、誰清理」的習慣,避免測試環境充斥垃圾資料。

明日預告

明白了測試資料管理的痛點與原則後,下一個核心問題隨之而來:
「在實務中,我們到底該用哪種策略來建立與清理測試資料?Fixture、Factory 還是 API 建立最好?」

明天 Day 17,我們將來深度探討:《 Day 17|測試資料的建立與清理策略:預建、API 產生與 Fixture 實戰 》


上一篇
Day 15|Retry不是解決Flaky test的方法:重試機制的陷阱與隔離策略
下一篇
Day 17|測試資料的建立與清理策略:預建、API 產生與 Fixture 實戰
系列文
從脆弱腳本到可信任測試平台:自動化測試架構30天23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言