BACK
Featured image of post VSCode MCP Servers 與專案導入 Playwright 自動化測試分享

VSCode MCP Servers 與專案導入 Playwright 自動化測試分享

研究並介紹 VSCode MCP Servers 與 Playwright,並在專案中導入 Playwright 跑自動化測試應用,分享並記錄研究過程。

官方文件 - Playwright

本篇將分享如何在 VSCode 開發中,結合 MCP Servers 與 Playwright,製作現代化自動化測試的流程。


MCP

MCP 全名為 Model Context Protocol,中文為「模型上下文協定」,是一種開放協定,2024 年 11 月由開發模型 Claude 的美國新創公司 Anthropic 推出,提供了一種標準化的方式,讓 AI 向外部工具請求使用服務和數據。並且透過提供 AI 上下文指示,讓 AI 決定使用哪些工具、按什麼順序以及如何將工作流程連結在一起以完成任務。

換句話說,MCP 不只能當作轉接頭,也像是提供 AI 一本使用說明書,幫助 AI 理解企業數據和應用情境,例如內部數據的結構與存取方式、重要的業務邏輯與規則,以及專業術語等。

以銀行客服系統導入 AI 為例,當客戶詢問:「能否提前還清貸款?」MCP 能提示 AI 該銀行的房貸規則在哪裡查詢、違約金計算方式,和哪些客戶符合提前還款條件等。

  • 典型應用:AI Copilot、Code Search、Test Runner、Remote Workspace 等
  • 讓 VSCode extension 能像微服務一樣,與外部系統互動

MCP Server 架構圖:

MCP 主機(MCP Host)

Claude、ChatGPT…等需要透過 MCP 存取外部工具的 AI 模型,它們會請求 MCP 來幫助執行各種操作。這些 AI 模型就像筆電本身,需要透過轉接頭來連接外部裝置。

例如:Claude、ChatGPT、Gemini、Llama…等 AI 模型。

MCP 客戶端(MCP Client)

AI 存取 MCP 的程式碼,它讓 AI 可以透過 MCP 來請求特定的工具或數據來源。這就像筆電的 USB 連接埠,負責發送請求給轉接頭,讓筆電知道該怎麼存取外部設備。

例如:Anthropic 官方 MCP SDK、OpenAI MCP client library、Python/TypeScript MCP client 實作…等。

MCP 伺服端(MCP Server)

Slack、Gmail、Google 日曆、Mac 檔案系統等,提供 AI 所需的功能與數據,可以是遠端應用程式或本地數據來源。MCP Server 就像滑鼠、鍵盤、硬碟等各種 USB 設備,透過轉接頭連接到筆電。

以 VSCode 為例,MCP Servers 作為 VSCode 與外部工具或數據源之間的媒介,像是使用者可以從 Google Drive 調取數據檔案,也可以從 GitHub 創建任務分支和查詢程式碼。

例如:Google Drive MCP Server、GitHub MCP Server、VSCode MCP Server、Notion MCP Server、Jira MCP Server…等。


VSCode MCP Server 是什麼?

VSCode 官方也提出了自己的 MCP Servers,目標是讓 extension 能以更彈性的方式與外部服務溝通,突破過去 LSP(Language Server Protocol)或 Debug Adapter Protocol 的限制。

MCP Server 特色:

  • 支援多種 context(如 AI、語意分析、測試、執行環境等)
  • 可同時處理多種資料流與事件,適合大型、複雜的 extension
  • 典型應用:AI Copilot、Code Search、Test Runner、Remote Workspace 等
  • 讓 VSCode extension 能像微服務一樣,與外部系統互動


Playwright 是什麼?

Playwright 是由 Microsoft 推出的現代化瀏覽器自動化測試框架,支援 Chromium、Firefox、WebKit 三大主流瀏覽器,並可用 TypeScript、JavaScript、Python、C# 等語言撰寫測試腳本。Playwright 具備跨平台、API 直覺、可錄製操作、支援多分頁與多瀏覽器等特性,適合用於前端、Web App、SPA、Webview 等多種場景的自動化測試。

主要特色:

Playwright 讓開發者能快速撰寫、執行、維護自動化測試腳本,是現代前端開發與測試的主流選擇之一。

Playwright 與其他自動化操作工具的差異

工具支援瀏覽器語言支援API/功能特色CI/CD 整合其他特色
PlaywrightChromium、Firefox、WebKit(含 Safari)JavaScript、TypeScript、Python、C#API 現代化且直覺,原生多分頁/多瀏覽器、網路攔截、等待同步、錄製腳本易於整合進階功能多
Selenium幾乎所有主流瀏覽器Java、Python、C#、JS 等API 較繁瑣、較舊,等待/同步需手動處理易於整合穩定性高、跨平台
CypressChromium、FirefoxJavaScript、TypeScriptAPI 現代化且直覺,原生等待/同步、無多分頁/多瀏覽器易於整合內建測試儀表板

重點差異說明:

  • Playwright 支援三大主流瀏覽器(含 Safari/WebKit),API 現代化,原生支援多分頁、網路攔截、等待同步,適合複雜互動測試。
  • Selenium 歷史悠久,支援最多語言與瀏覽器,但 API 較繁瑣,等待/同步需手動處理,適合跨平台、穩定性要求高的場景。
  • Cypress 以前端 E2E 測試為主,API 直覺,原生等待,但僅支援部分瀏覽器,不支援多分頁,適合前端 SPA/React/Vue 等專案。

Playwright 在現代前端、Web App、VSCode extension 測試場景下,通常能提供更高效、易用的自動化體驗。


Playwright 導入方式

安裝 Playwright 套件

1
2
npm install -D @playwright/test
npx playwright install

這會安裝 Playwright 及所需瀏覽器。

建立測試目錄與設定檔

建立 tests/ 目錄,並新增 playwright.config.ts

playwright.config.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { defineConfig, devices } from "@playwright/test"

export default defineConfig({
  testDir: "./playwright", // 測試腳本目錄
  timeout: 30 * 1000,
  expect: {
    timeout: 5000
  },
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [["list"], ["html", { outputFolder: "playwright-report", open: "never" }]],
  use: {
    baseURL: "https://localhost:8082/", // Vite dev server https
    ignoreHTTPSErrors: true,
    trace: "on-first-retry",
    headless: false,
    viewport: { width: 1280, height: 800 },
    ...devices["Desktop Chrome"]
  }
})

撰寫第一個測試腳本

例如建立一支跑自動跑登入流程的腳本:

tests/login.spec.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import { test, expect } from '@playwright/test';

test("登入流程測試", async ({ page }) => {
  await page.goto("https://{我要測試的網站}/login");
  await page.fill("input[name="username"]", "wayne");
  await page.fill("input[name="password"]", "123456");
  await page.click("button[type="submit"]");
  await expect(page).toHaveURL(/dashboard|home/);
  await expect(page.locator(".user-info")).toContainText("wayne");
});

執行測試

1
2
3
4
5
6
7
8
# 執行所有測試:
npx playwright test

#執行單一測項(指定檔案):
npx playwright test tests/login.spec.ts

# 執行單一測項(指定測試名稱):
npx playwright test -g "登入流程測試"

Playwright 會自動啟動瀏覽器並執行所有或指定的測試腳本。

進階設定與 CI 整合

  • 可在 playwright.config.ts 設定多瀏覽器測試、環境變數、報告格式等
  • 可在 GitHub Actions、GitLab CI 等自動化流程中執行 Playwright 測試

Playwright 在 VSCode Extension/MCP Server 專案的應用

  • 驗證 extension Webview UI/互動
  • 測試 MCP Server API 回應與錯誤處理
  • 整合 CI/CD,確保 extension 發布品質

Playwright 讓開發者能快速撰寫、執行、維護自動化測試腳本,是現代前端開發與測試的主流選擇之一。

Playwright 的設定值與常用元件介紹

playwright.config 常用設定值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests', // 測試檔案目錄
  timeout: 30000, // 單一測試逾時(毫秒)
  retries: 1, // 測試失敗時重試次數
  reporter: [['list'], ['html']], // 測試報告格式
  fullyParallel: true, // 是否完全平行執行所有測試
  forbidOnly: true, // 禁止 .only,避免漏跑測試
  workers: 2, // 平行執行的 worker 數量
  outputDir: 'test-results/', // 測試產生的快照、截圖、trace 等輸出目錄
  testIgnore: ['**/skip/**'], // 忽略哪些測試檔案(glob pattern)
  testMatch: ['**/*.spec.ts', '**/*.test.ts'], // 匹配哪些測試檔案(glob pattern)
  offline: false, // 是否離線模式(true | false)
  use: {
    headless: true, // 是否無頭模式
    baseURL: 'http://localhost:3000', // 預設 base URL
    viewport: {
      width: 1280,
      height: 720,
    }, // 預設瀏覽器視窗大小
    ignoreHTTPSErrors: true, // 忽略 HTTPS 錯誤
    video: 'retain-on-failure', // 錄影設定: 'on' | 'off' | 'retain-on-failure'
    screenshot: 'only-on-failure', // 截圖設定: 'on' | 'off' | 'only-on-failure'
    trace: 'retain-on-failure', // Trace 記錄: 'on' | 'off' | 'retain-on-failure'
    storageState: 'state.json', // 預載入登入狀態檔
    locale: 'zh-TW', // 預設語系
    colorScheme: 'dark', // 顏色主題: 'light' | 'dark' | 'no-preference'
    permissions: [
      'geolocation',
      'notifications',
    ], // 權限設定
    geolocation: {
      latitude: 25.033,
      longitude: 121.5654,
    }, // 預設地理位置
    timezoneId: 'Asia/Taipei', // 預設時區
    userAgent: 'MyCustomAgent/1.0', // 自訂 User-Agent
    httpCredentials: {
      username: 'user',
      password: 'pass',
    }, // HTTP 基本認證
    extraHTTPHeaders: {
      'x-test-header': 'test',
      'authorization': 'Bearer token',
    }, // 額外 HTTP 標頭
    acceptDownloads: true, // 是否允許下載檔案
    isMobile: false, // 是否模擬行動裝置(true | false)
    deviceScaleFactor: 1, // 裝置像素比例(預設 1)
    hasTouch: false, // 是否支援觸控(true | false)
    javaScriptEnabled: true, // 是否啟用 JS(true | false)
    recordVideo: {
      dir: 'videos/',
      size: { width: 1280, height: 720 },
    }, // 錄影輸出目錄與尺寸
    serviceWorkers: 'allow', // Service Worker 設定: 'allow' | 'block'
    strictSelectors: true, // 是否嚴格選取器(true | false)
    userDataDir: './user-data', // 使用者資料目錄
    launchOptions: {
      slowMo: 100,
      // 其他啟動選項可加在這裡
    }, // 啟動瀏覽器時的額外選項
  },
  projects: [ // 多瀏覽器/多環境測試設定
    {
      name: 'chromium',
      use: { browserName: 'chromium' },
    },
    {
      name: 'firefox',
      use: { browserName: 'firefox' },
    },
    {
      name: 'webkit',
      use: { browserName: 'webkit' },
    },
  ],
  globalSetup: './global-setup.js', // 全域初始化腳本
  globalTeardown: './global-teardown.js', // 全域清理腳本
});

更多請參考官方文件

常用元件與方法

類型名稱/方法說明
測試物件test定義測試案例
頁面物件page操作瀏覽器分頁
斷言expect驗證結果
定位器locator精準選取元素
填值fill填入表單欄位
點擊click點擊按鈕或元素
取得文字textContent取得元素文字內容
等待waitForSelector等待元素出現
螢幕截圖screenshot擷取頁面或元素畫面
輸入鍵盤keyboard鍵盤事件
輸入滑鼠mouse滑鼠事件
請求物件requestAPI 請求測試
瀏覽器物件browser控制瀏覽器(多分頁、多瀏覽器)
配置defineConfigPlaywright 設定檔
錯誤截圖trace錯誤追蹤與錄影
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 定義測試案例 test
test('首頁載入', async ({ page }) => {
  await page.goto('https://example.com');
});

// 操作瀏覽器分頁 page
await page.goto('https://example.com');

// 驗證結果 expect
await expect(page).toHaveURL('https://example.com');

// 精準選取元素 locator
const btn = page.locator('button.login');

// 填入表單欄位 fill
await page.fill('input[name="username"]', 'wayne');

// 點擊按鈕或元素 click
await page.click('button[type="submit"]');

// 取得元素文字內容 textContent
const txt = await page.textContent('.user-info');

// 等待元素出現 waitForSelector
await page.waitForSelector('.loading-done');

// 擷取頁面或元素畫面 screenshot
await page.screenshot({ path: 'home.png' });

// 鍵盤事件 keyboard
await page.keyboard.press('Enter');

// 滑鼠事件 mouse
await page.mouse.click(100, 200);

// API 請求測試 request
const res = await request.get('/api/health');

// 控制瀏覽器 browser
const browser = await chromium.launch();

// Playwright 設定檔 defineConfig
export default defineConfig({ testDir: './tests' });

// 錯誤追蹤與錄影 trace
npx playwright show-trace trace.zip

更多元件與 API 可參考 Playwright 官方文件


實戰一:自動化測試 API healthz check

  1. 建立 MCP Server 專案(Node.js/TypeScript)
  2. 在 extension 內啟動 MCP Server,並與前端溝通
  3. 撰寫 Playwright 測試腳本,模擬 VSCode extension 與 MCP Server 的互動
1
2
3
4
5
6
7
8
9
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
  testDir: './tests',
  use: {
    headless: true,
    baseURL: 'http://localhost:3000',
  },
});
1
2
3
4
5
6
// tests/mcp-server.spec.ts
import { test, expect } from '@playwright/test';
test('MCP Server health check', async ({ request }) => {
  const res = await request.get('/health');
  expect(res.ok()).toBeTruthy();
});

實戰二:Playwright 自動化流程範例

以下以我自己的專案的登入流程自動化測試為例 透過打開我的網站登入頁,並輸入帳號與密碼後進行登入,登入後檢查頁面上是否存在「Dashboard」字樣(因我的網站登入後會導向到 Dashboard 頁面)。

tests/login.spec.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { test, expect } from '@playwright/test';

test('登入流程測試', async ({ page }) => {
  await page.goto('https://{我的網站}/');
  await page.fill('input[name="username"]', 'wayne');
  await page.fill('input[name="password"]', '123456');
  await page.click('button[type="submit"]');
  // 驗證是否登入成功
  await expect(page).toHaveURL(/dashboard|home/);
  await expect(page.locator('.user-info')).toContainText('wayne');
});

封裝成一個 helper 好了:

login-helper.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { chromium, Page } from "@playwright/test"

export async function doLogin(page: Page, saveStorageStatePath?: string) {
  await page.goto("https://{我的網站}/")
  await page.fill('input[name="username"], input[placeholder="帳號"], input[type="text"]', "wayne")
  await page.fill('input[name="password"], input[placeholder="密碼"], input[type="password"]', "123456")
  await page.click('button[type="button"]')
  await page.waitForSelector("text=Dashboard")
  if (saveStorageStatePath) {
    await page.context().storageState({ path: saveStorageStatePath })
  }
}

// 若直接執行本檔案,則自動產生 storageState.json
if (require.main === module) {
  ;(async () => {
    const browser = await chromium.launch()
    const page = await browser.newPage()
    await doLogin(page, "playwright/storageState.json")
    await browser.close()
  })()
}

延伸製作一個 登入並切換語系 的自動化測試腳本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { test, expect } from "@playwright/test"
import { doLogin } from "./login-helper"
test("測試登入流程與切換語系功能", async ({ page }) => {
  await test.step("測試登入流程環節", async () => {
    await doLogin(page)
    await page.waitForTimeout(1000)
    await page.screenshot({ path: "debug-login.png" })
    await expect(page.locator("text=Dashboard").first()).toBeVisible()
  })

  await test.step("測試切換語系為简体中文", async () => {
    await page.locator("#btn-lang").hover()
    await page.locator(".v-list-item-title", { hasText: "简体中文" }).click()

    // 驗證文字是否變成簡體中文了
    await expect(page.locator("text=仪表板").first()).toBeVisible()
  })

  await test.step("測試切換語系為英文", async () => {
    await page.locator("#btn-lang").hover()
    await page.locator(".v-list-item-title", { hasText: "English" }).click()

    // 驗證文字是否變成英文了
    await expect(page.locator("text=Dashboard").first()).toBeVisible()
  })

  await test.step("測試切換語系為繁體中文", async () => {
    await page.locator("#btn-lang").hover()
    await page.locator(".v-list-item-title", { hasText: "繁體中文" }).click()

    // 驗證文字是否變成繁體中文了
    await expect(page.locator("text=儀錶板").first()).toBeVisible()
  })
})

實戰三:Playwright 串聯 Vitest UI 跑單元測試

串聯一下之前在闡述單元測試、元件測試,並學習在自己的 Vue3 專案中加入 Vitest!,自己撰寫的 Utils tools function 的單元測試好了,讓自己後續可以直接打打字,讓 Agent 幫我跑這些單元測試(工程師沒有最懶,只有更懶)。

test-tools-unit.spec.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { test, expect } from "@playwright/test"

test("執行 tools.test.ts 所有 Vitest 單元測試", async ({ page }) => {
  // 檢查 Vitest UI 服務
  try {
    const response = await page.goto("http://localhost:51204/__vitest__/")
    if (!response || !response.ok()) {
      throw new Error("Vitest UI 服務未啟動或無法連線,請先執行 npx vitest --ui")
    }
  } catch (e) {
    throw new Error("Vitest UI 服務未啟動或無法連線,請先執行 npx vitest --ui")
  }

  // 搜尋 tools.test.ts
  await page.fill('input[placeholder="Search..."]', "tools.test")
  await page.keyboard.press("Enter")

  // 點擊 tools.test.ts 測試檔
  await page.click("text=tools.test.ts")

  // 點擊 rerun file 按鈕
  const rerunButton = await page.$('button[title="Rerun file"], button[aria-label="Rerun file"]')
  if (rerunButton) await rerunButton.click()

  // 等待測試進入 running 狀態
  await page.waitForSelector("text=Running", { timeout: 10000 }).catch(() => {})

  // 等待 running 消失
  await page.waitForSelector("text=Running", { state: "detached", timeout: 60000 }).catch(() => {})

  // 等待「All tests passed in this file」出現以驗證單元測試是否通過
  await page.waitForSelector("text=All tests passed in this file", { timeout: 20000 })
  const successText = await page.textContent("text=All tests passed in this file")
  
  console.log("測試摘要:", successText)
  expect(successText).toContain("All tests passed in this file")
})

* 流程非常的彈性可控,且可以依照自己的需求去撰寫測試腳本,達到自動化測試的目的,以上就舉幾個較為單純的腳本內容,可以依各自業務需求去撰寫自己需要的腳本。
* 若你的 VSCode 有開啟 chat.mcp.enabled,則可直接透過 VSCode ChatAgent 模式進行即時的測試腳本撰寫與調試,加速完成你的測試項目腳本,甚至直接透過 Chat 來執行你的測試項目。
* 可以在開發時,多在 DOM 上埋入 id、class,這樣當執行自動化驗證的操作時,可以更輕鬆的獲取到你所需要的元素,確保自動化測試流程的穩定與品質。


開發心得與建議

  • MCP Server 架構彈性高,適合 AI/自動化/大型 extension。
  • 建議在開發前端代碼時,在 DOM 上多埋入 id、class,方便 Playwright 定位元素,確保自動化測試流程的穩定與品質。
  • 建議可以透過 VSCode Chat 的 Agent 模式進行即時的測試腳本撰寫與調試,記得 vscode 的settings 要開啟 chat.mcp.enabled
  • Playwright 腳本可直接整合 CI,快速驗證功能。
  • 建議在使用 VSCode 時,安裝 Playwright 的官方 extension,方便在 VSCode 內撰寫、執行測試腳本。
  • 建議將 MCP Server、Playwright 測試與 extension 專案分層管理,方便維護。
  • Playwright 支援多瀏覽器測試,建議在 CI 中同時驗證 Chrome/Firefox/WebKit。

結語

VSCode MCP Servers 與 Playwright 的結合,讓 extension 開發與測試更現代化、流程自動化。推薦給有志於 VSCode 生態圈、AI 工具、測試自動化的開發者!

如有相關問題,歡迎留言討論或參考官方文件。


comments powered by Disqus