自動化優先應用設計架構及最佳實踐

從測試人員的角度出發,提出了一個理念,即開發者如何從一開始就將應用程式設計得便於自動化測試。

引言

作為一名自動化測試人員,我花了無數個小時與那些似乎故意設計成抗拒自動化的應用程式作鬥爭。脆弱的選擇器稍有UI調整就會失效,元件缺乏可辨識的屬性,複雜的流程隱藏在不明確的狀態背後──這些都是我們每天都會遇到的挫折。

但關鍵在於:大多數這類挑戰都是可以避免的。如果開發人員從一開始就將自動化納入應用程式的設計考量,測試就會變得更快、更可靠,也更容易維護。

這不僅僅是為了讓我們的工作更輕鬆——而是為了更快地交付更高品質的軟體。

本文分享了一些實用建議,每個應用程式開發人員都應該遵循這些建議,以使他們的應用程式「做好自動化準備」。

使元素可識別且穩定

問題:

許多應用程式僅依賴每次建置都會改變的 CSS 類別名稱或產生的 ID。而像 Selenium、Playwright 和 Appium 這樣的自動化框架則依賴定位器(唯一識別碼)來與元素互動。

開發者該怎麼做:

  • 新增 data-testid 互動元素(按鈕、輸入框、表單、連結)的屬性
  • 保持這些 ID 在各個版本之間穩定——像對待公共 API 一樣對待它們。
  • 避免僅依賴動態類別名稱或 XPath 位置

真實範例:

// ❌ BAD - Changes frequently 

<button className="btn-primary-v2 btn-submit-form"> 

Submit 

</button> 

// ✅ GOOD - Stable identifier for testers 

<button data-testid="form-submit-button" className="btn-primary-v2"> 

Submit 

</button>

提交

為何如此重要:

當你添加 data-testid測試人員可以使用以下方法可靠地定位元素 page.locator('[data-testid="form-submit-button"]') 在劇作家或 find_element(By.CSS_SELECTOR, '[data-testid="form-submit-button"]') 在 Selenium 中。

使用語義化的HTML和正確的標籤關聯

問題:

通用 <div> 以及 <span> 沒有標籤的元素使得測試人員幾乎不可能理解控制項的用途,輔助功能工具也無法正常運作。

開發者該怎麼做:

  • 使用語意化的HTML元素: <button>, <input>, <label>, <form>
  • 使用以下方式將標籤與輸入關聯起來 <label for="inputId">
  • 使用 aria-label or aria-labelledby 對於複雜部件

真實範例:

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

測試人員視角:

借助語義化的 HTML,我可以使用基於角色的選擇器:page.locator('input[role=”checkbox”]') 甚至在 Playwright 中使用 getByRole(“checkbox”, { name: /enable notifications/i })——使測試更易讀,並且能夠更好地適應樣式更改。

實現顯式等待狀態和載入指示器

問題:

使用大量 JavaScript 的應用程式會動態載入內容。測試人員經常會遇到測試不穩定的情況,因為元素的出現/消失是異步的,他們無法確定頁面何時才真正「準備就緒」。

開發者該怎麼做:

  • 使用 data-testid 用於載入狀態:
    <div data-testid="loading-spinner"> 
  • 使用狀態屬性標記內容部分: aria-busy="true" 負載期間
  • 確保應用程式達到穩定狀態後再響應用戶交互
  • 提供明確的加載完成訊號

真實範例:

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

測試自動化程式碼(劇作家):

// Wait for loading to complete 

await page.locator('[data-testid="loading-spinner"]').waitFor({ state: 'hidden' }); 

 

// Now interact with the list 

const userList = page.locator('[data-testid="user-list"]'); 

await expect(userList).toBeVisible();

設計具有清晰輸入處理功能的表單 

問題: 

複雜的表單,如果錯誤狀態不明確、存在隱藏的驗證或輸入行為不規範,會使自動化腳本感到困惑。 

開發者該做什麼: 

  • 使用帶有清晰名稱屬性的標準 HTML 表單元素 
  • 提供明確的錯誤訊息 data-testid 標記 
  • 確保表單驗證結果可觀察(而非靜默失敗) 
  • 包含成功確認訊息。 

真實例子: 

// ✅ GOOD - Clear form with observable states 

<form data-testid="login-form"> 

<div> 

<label htmlFor="email-input">Email:</label> 

<input 

id="email-input" 

name="email" 

type="email" 

data-testid="email-input" 

required 

/> 

{errors.email && ( 

<span data-testid="email-error" role="alert"> 

{errors.email} 

</span> 

)} 

</div> 

 

<div> 

<label htmlFor="password-input">Password:</label> 

<input 

id="password-input" 

name="password" 

type="password" 

data-testid="password-input" 

required 

/> 

</div> 

 

<button  

type="submit"  

data-testid="login-submit-button" 

disabled={isSubmitting} 

> 

{isSubmitting ? 'Logging in...' : 'Login'} 

</button> 

 

{submitSuccess && ( 

<div data-testid="login-success-message" role="status"> 

Login successful! Redirecting... 

</div> 

)} 

</form>

測試自動化程式碼(Selenium):

from selenium import webdriver 

from selenium.webdriver.common.by import By 

from selenium.webdriver.support.ui import WebDriverWait 

from selenium.webdriver.support import expected_conditions as EC 

 

driver = webdriver.Chrome() 

driver.get("https://app.com/login") 

 

# Fill form 

driver.find_element(By.CSS_SELECTOR, '[data-testid="email-input"]').send_keys("test@example.com") 

driver.find_element(By.CSS_SELECTOR, '[data-testid="password-input"]').send_keys("password123") 

 

# Submit 

driver.find_element(By.CSS_SELECTOR, '[data-testid="login-submit-button"]').click() 

 

# Wait for success message 

WebDriverWait(driver, 10).until( 

EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="login-success-message"]')) 

)

使 API 響應可預測且一致

問題:

依賴 API 回應的行動和 Web 應用需要穩定的資料結構。不一致或不可預測的 API 回應會導致測試隨機失敗。

開發者該怎麼做:

  • 所有介面均使用一致的 JSON 結構。
  • 在每個物件中包含唯一識別碼(ID)
  • 文件 API 契約(OpenAPI/Swagger)
  • 傳回有意義的 HTTP 狀態碼和錯誤訊息
  • 在開發過程中測試 API 回應

真實範例:

// ✅ GOOD - Predictable API response 

{ 

"success": true, 

"data": { 

"id": "user-123", 

"email": "test@example.com", 

"name": "John Doe", 

"created_at": "2026-03-01T10:00:00Z" 

}, 

"timestamp": "2026-03-22T14:30:00Z" 

} 

 

// ❌ BAD - Inconsistent response 

{ 

"status": "ok", 

"user": { 

"uid": "123", 

"email_address": "test@example.com" 

}, 

"meta": { "time": 1679574600 } 

}

測試人員的行動自動化(Appium):

# With predictable API, testers can mock and validate 

from appium import webdriver 

 

driver = webdriver.Remote("http://localhost:4723/wd/hub", capabilities) 

 

# Mock the API response 

mock_response = { 

"success": True, 

"data": { 

"id": "user-123", 

"name": "Test User" 

} 

} 

 

# Now reliably access user data in the app 

user_name_element = driver.find_element("xpath", "//text[@value='Test User']")

提供便於測試的配置和模式

問題:

生產環境中的分析、廣告、第三方追蹤器和網路延遲等功能可能會幹擾測試執行。

開發者該怎麼做:

  • 提供測試模式或環境變量
  • 允許在測試環境中停用分析、廣告和追蹤功能
  • 支援跳過不必要的動畫或延遲
  • 提供 API 端點以重置應用程式狀態進行測試

實際範例(React):

// ✅ GOOD - Test-friendly configuration 

const isTestMode = process.env.REACT_APP_TEST_MODE === 'true'; 

 

function App() { 

return ( 

<div> 

{!isTestMode && <Analytics />} 

{!isTestMode && <Ads />} 

<MainContent /> 

</div> 

); 

}

環境變數:

REACT_APP_TEST_MODE=true npm start

文件自動化預期

問題:

測試人員會花費時間進行逆向工程,研究如何與應用程式交互,特別是自訂元件。

開發者該怎麼做:

  • 文件 data-testid README 文件中的命名約定
  • 為複雜功能提供測試/自動化指南
  • 分享組件文檔(Storybook 等)
  • 列出測試人員應該了解的所有自訂事件和回調函數

範例文件:

## Automation Testing Guide 

 

### Naming Conventions 

- Buttons: `data-testid="[feature]-[action]-button"` (e.g., `user-logout-button`) 

- Form inputs: `data-testid="[form]-[field]-input"` (e.g., `login-email-input`) 

- Lists: `data-testid="[list]-container"` and items as `[list]-item-[id]` 

 

### Key Components 

- **Modal**: `.modal-overlay` (hidden when closed), contains `data-testid="modal-close-button"` 

- **Dropdown**: Use semantic `<select>` or `aria-haspopup="listbox"` 

- **Date Picker**: Supports keyboard navigation and `aria-label` 

 

### Test Environment Setup 

Set `API_URL=http://localhost:3001/api` to use mock server

開發者的關鍵要點

測試人員需要什麼 開發者該做什麼
穩定元素標識符 使用 data-testid 屬性
清晰的組件層級結構 使用語義化 HTML
可觀察的載入/就緒狀態 提供帶有標識符的載入指示器
可預測的形式行為 文件驗證和使用標準元素
一致的 API 回應 遵循 API 合約和文件結構
有利於測試的​​環境 支援測試模式,停用廣告/分析
清晰的自動化指導 記錄您的自動化預期

結語

建立可自動化應用的關鍵不在於規避限制,而在於從一開始就正確地設計應用。

當開發人員優先考慮清晰度、一致性和可觀察性時,每個人都會受益——測試人員編寫速度更快、更可靠的測試;使用者獲得更高品質的軟體;整個團隊的行動也更加迅速。

好消息是?這些做法本身就是優秀的軟體工程實務。語意化的 HTML、可預測的 API 和清晰的錯誤處理不僅有利於自動化測試,還能提升可存取性、效能和使用者體驗。

開始 data-testid 新增屬性、使用語意化的 HTML、顯示載入狀態並編寫元件文件。你的自動化測試人員會感謝你,你的應用程式也會因此變得更好。

資源和工具

你可能還喜歡