> 九號工具站
返回列表

Self-healing Tests with LLM — AI 自動修壞掉的 Selector

Self-healing test 完整指南。LLM 自動修壞掉的 selector、Mabl / Functionize 比較、自建方案、CI 整合、何時該用 / 不該用、與傳統 selector 策略對比。

QA self-healing ai-selector mabl functionize llm-tests
· 最後更新 2026-06-17 · 最後審閱 2026-08-30

💡 本文原刊於 qa.9niche.com,2026-08 併入 9niche.com 懶人包,內容照原文完整搬遷。

1. 前言

「跑 E2E 的時間 20%、修 selector 的時間 80%」是 QA 痛點 Top 3。Self-healing test 用 LLM 自動找出新 selector、把維護時間砍掉 70%。這篇給你完整工具地圖 + 自建方案。

2. 為什麼 selector 會壞

flowchart LR UI[UI 改動] --> Sel{Selector 壞?} Sel --> B1["class 改名"] Sel --> B2["DOM 結構重組"] Sel --> B3["元素位置變"] Sel --> B4["A/B test 新版本"] Sel --> B5["CSS-in-JS hash 變"] Sel --> Fix[QA 修] Fix --> Time["每週 5-8 小時"] Time --> Cost["年 $20-30k 工時成本"] style Time fill:#ef4444,color:#fff style Cost fill:#ef4444,color:#fff

3. Self-healing 工作流

關鍵:self-heal 後必須 alert + review、不能靜默修復、否則會掩蓋真 bug。

flowchart TD Run[跑 test] --> Find[找 selector] Find --> Q{找到?} Q -->|是| Pass[Pass] Q -->|否| LLM[LLM 介入] LLM --> Analyze[分析周圍 DOM] Analyze --> Suggest[建議新 selector] Suggest --> Try[Try 新 selector] Try --> Q2{有元素?} Q2 -->|是| Continue[繼續 test + 標記] Q2 -->|否| Fail[Fail] Continue --> Alert[Slack 通知 QA] Alert --> Review[人工 review 是 cosmetic 還 bug] style Continue fill:#10b981,color:#fff style Alert fill:#f59e0b,color:#fff style Fail fill:#ef4444,color:#fff

4. 商用工具比較

工具起跳價強項弱項
Mabl$200/月完整 E2E platform、UX 好鎖定平台
Functionize企業高 AI 比重
Testim (Tricentis)$450/月大廠資源老牌 UX
Reflect.run$69/月便宜起跳功能少
BugBug免費起跳起步 OK規模化弱

5. 自建 Self-healing — Playwright + Claude

核心思路

1. 寫 wrapper 攔截 selector 失敗
2. 失敗時拿 DOM snapshot + 原 selector
3. 餵給 LLM「依照原 selector 意圖、在新 DOM 找替代」
4. 試新 selector
5. 成功 → 繼續 + 記 log
6. 失敗 → 報錯

實作範例

import { Page, Locator } from '@playwright/test';
import Anthropic from '@anthropic-ai/sdk';

const claude = new Anthropic();

async function smartLocator(page: Page, selector: string, hint?: string): Promise<Locator> {
  try {
    const loc = page.locator(selector);
    await loc.waitFor({ timeout: 3000 });
    return loc;
  } catch {
    // Selector 找不到 → 啟動 self-healing
    console.warn(`⚠️ Selector "${selector}" not found, trying AI...`);

    const html = await page.content();
    const truncated = html.slice(0, 30000);  // 控制 token

    const response = await claude.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 200,
      messages: [{
        role: 'user',
        content: `原 selector: ${selector}
${hint ? `用途: ${hint}` : ''}

下面是當前 HTML。請建議一個能找到「同一元素」的新 selector。只回 selector 字串、不要解釋。

HTML:
${truncated}`,
      }],
    });

    const newSelector = response.content[0].text.trim();
    console.log(`🤖 AI 建議新 selector: ${newSelector}`);

    // 記到 healing log
    await logHeal(selector, newSelector);

    return page.locator(newSelector);
  }
}

// 使用
test('login with self-healing', async ({ page }) => {
  await page.goto('/login');
  const loginBtn = await smartLocator(page, '#login-btn', '登入按鈕');
  await loginBtn.click();
});

healing log

import fs from 'fs';

async function logHeal(oldSel: string, newSel: string) {
  const log = {
    at: new Date().toISOString(),
    old_selector: oldSel,
    new_selector: newSel,
    file: __filename,
  };
  fs.appendFileSync('healing.log', JSON.stringify(log) + '\n');

  // 也發 Slack
  await fetch(process.env.SLACK_WEBHOOK!, {
    method: 'POST',
    body: JSON.stringify({
      text: `🤖 Self-heal: ${oldSel} → ${newSel}`,
    }),
  });
}

6. Slack alert 範例

🤖 Self-heal triggered

File: tests/login.spec.ts
Old selector: #login-btn
New selector: button[data-testid="submit-login"]
Time: 2026-06-17 14:32

Action needed:
- Was this a planned UI change? → Update test
- Was this a regression? → Report bug

7. 跟 Page Object Model 並存

// pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}

  // 用 smart locator wrapper
  get emailInput() { return smartLocator(this.page, '[name=email]', 'Email 輸入框'); }
  get passwordInput() { return smartLocator(this.page, '[name=password]', 'Password 輸入框'); }
  get submitBtn() { return smartLocator(this.page, 'button[type=submit]', '送出按鈕'); }

  async login(email: string, password: string) {
    await (await this.emailInput).fill(email);
    await (await this.passwordInput).fill(password);
    await (await this.submitBtn).click();
  }
}

POM 給結構、self-healing 給彈性 = 完美組合

8. CI 整合

name: E2E with Self-Healing
on: [pull_request]

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx playwright test
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

      # 收集 healing log
      - if: always()
        uses: actions/upload-artifact@v4
        with:
          name: healing-log
          path: healing.log

      # 多於 X 次 heal → fail PR
      - if: always()
        run: |
          COUNT=$(wc -l < healing.log)
          if [ $COUNT -gt 10 ]; then
            echo "❌ Too many self-heals ($COUNT) — likely UI mass change"
            exit 1
          fi

9. 何時該用 / 不該用

flowchart TD Q{該用 self-healing?} --> Q1{E2E 數量?} Q1 -->|< 30| No1[手動修還可] Q1 -->|30-100| Yes1[Yes - 自建] Q1 -->|100+| Yes2[Yes - Mabl 等商用] Q --> Q2{UI 改動頻率?} Q2 -->|穩定| No2[CP 值低] Q2 -->|每週改| Yes3[強烈推薦] Q --> Q3{安全要求?} Q3 -->|金融 / 醫療| Caution[小心 - 別靜默修復] Q3 -->|一般 SaaS| Free[放心用] style Yes1 fill:#10b981,color:#fff style Yes2 fill:#10b981,color:#fff style Yes3 fill:#10b981,color:#fff style Caution fill:#f59e0b,color:#fff

10. 反模式

成本控制

// 每次 heal 約 $0.02-0.05(Claude Sonnet)
// 100 個 E2E、每月 50 次 heal = $2.50/月 — 划算

// 但無 budget cap 會炸
const MAX_HEALS_PER_RUN = 20;
let healCount = 0;

async function smartLocator(...) {
  if (healCount >= MAX_HEALS_PER_RUN) {
    throw new Error(`Heal budget exceeded (${MAX_HEALS_PER_RUN})`);
  }
  healCount++;
  // ...
}
flowchart TD Anti[Self-healing 反模式] --> A1["完全信任 AI、靜默修復"] Anti --> A2["沒設 budget cap、API 費用爆"] Anti --> A3["把 selector 全部寫死等 AI 修"] Anti --> A4["不分析 heal log、不更新 test"] Anti --> A5["在金融 / 醫療系統用、沒 audit"] Anti --> A6["取代 review、QA 看都不看"] style A1 fill:#ef4444,color:#fff style A2 fill:#ef4444,color:#fff style A3 fill:#ef4444,color:#fff style A4 fill:#ef4444,color:#fff style A5 fill:#ef4444,color:#fff style A6 fill:#ef4444,color:#fff

11. 給 QA 的 5 句

  1. Self-healing 解的是 cosmetic 變化、不是業務 bug
  2. 永遠 alert + log、不要靜默修復
  3. POM + Self-healing = E2E 維護地獄解決組合
  4. 設 budget cap、API 費用會爆
  5. 金融 / 醫療要 audit log、別當救命神器

12. 最後

Self-healing test 是 2026 後 QA 維護生產力的 game-changer。自動化 selector 飄移、保留人類判斷力。從 30 個 case 自建 wrapper 起步、3 個月後你會把維護時間從每週 8 小時砍到 2 小時。

延伸:

ℹ️

一般聲明

本站提供之資訊僅供參考,不保證其完整性與正確性。使用者應自行判斷資訊之適用性。

意見反饋