Cypress 入門 + vs Playwright 完整對比

「我們公司用 Cypress、面試問 Playwright 我不會、該轉嗎?」 — 看到太多 QA 卡在這。實話:Cypress 還很好用、但 Playwright 全方位贏。這篇講清楚兩者差異 + 何時該轉。

一張圖看完差異

flowchart TD
    Choose{選 framework} --> Q1{多瀏覽器?}
    Q1 -->|Chrome only| Cyp1[Cypress 可]
    Q1 -->|Safari / WebKit| PW1[Playwright]

    Choose --> Q2{Cross-origin?}
    Q2 -->|不需要| Cyp2[Cypress 可]
    Q2 -->|要 OAuth / 多 domain| PW2[Playwright]

    Choose --> Q3{多 tab / iframe?}
    Q3 -->|不需要| Cyp3[Cypress 可]
    Q3 -->|要| PW3[Playwright]

    Choose --> Q4{平行跑 CI 預算?}
    Q4 -->|沒限制| Both[都行]
    Q4 -->|要免費| PW4[Playwright]

    style PW1 fill:#10b981,color:#fff
    style PW2 fill:#10b981,color:#fff
    style PW3 fill:#10b981,color:#fff
    style PW4 fill:#10b981,color:#fff
    style Cyp1 fill:#06b6d4,color:#fff
    style Cyp2 fill:#06b6d4,color:#fff
    style Cyp3 fill:#06b6d4,color:#fff

Cypress 30 分鐘入門

安裝

npm init -y
npm install --save-dev cypress
npx cypress open

GUI 介面選「E2E Testing」、Cypress 會自動建好 demo。

第一個 test

// cypress/e2e/login.cy.js
describe('Login', () => {
  beforeEach(() => {
    cy.visit('https://app.example.com/login');
  });

  it('登入成功跳轉 dashboard', () => {
    cy.get('input[name=email]').type('[email protected]');
    cy.get('input[name=password]').type('Test@123');
    cy.contains('button', '登入').click();
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome').should('be.visible');
  });

  it('錯密碼顯示錯誤', () => {
    cy.get('input[name=email]').type('[email protected]');
    cy.get('input[name=password]').type('wrong');
    cy.contains('button', '登入').click();
    cy.contains('帳號或密碼錯誤').should('be.visible');
  });
});

跑:

npx cypress run                  # headless
npx cypress run --browser chrome # 指定瀏覽器
npx cypress open                 # GUI 互動

Cypress 最大強項 — Time Travel Debugger

flowchart LR
    Tests[跑 test] --> Snap[每步 snapshot DOM]
    Snap --> GUI[GUI 介面]
    GUI --> Click[點任一步 → 看當下 DOM]
    Click --> Console[Console 直接 query]

    style GUI fill:#a855f7,color:#fff
    style Console fill:#10b981,color:#fff

這個 UX 至今沒對手。Playwright trace viewer 接近但體驗仍差一截。

Cypress vs Playwright 全方位對比

維度 Cypress Playwright 贏家
學習曲線 低(chainable API 直覺) 中(async/await 標準) Cypress
Debug UX 神級 time travel 好的 trace viewer Cypress
多瀏覽器 Chrome / Edge / Firefox + WebKit (Safari) Playwright
Cross-origin 受限(需 setting) 原生支援 Playwright
Multi-tab / window 困難 / hack 原生 Playwright
iframe 困難 原生 page.frame() Playwright
平行測試 要付費 Cypress Cloud 免費內建 sharding Playwright
API 測試 cy.request() request fixture 平手
Component testing Cypress
Mobile emulation 受限 Playwright
Auto-wait 平手
TypeScript 支援 一等公民 Playwright
CI 整合 更好 Playwright
企業支援 Cypress Cloud Microsoft 支持 Playwright
社群活躍 大但減 暴增中 Playwright
NPM downloads 5M/week 12M/week (2026) Playwright

Selector 哲學差異

// Cypress — chainable + 自家 API
cy.get('button')
  .contains('Submit')
  .click();

cy.get('[data-testid=email]')
  .type('[email protected]');

// Playwright — standard async/await
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByLabel('Email').fill('[email protected]');

Playwright 用 getByRole 跟 accessibility tree 走、跨 UI 改動更穩Cypress 鼓勵 data-testid、要前端配合

Cross-origin 痛點實戰

// Cypress — OAuth 登入會死
it('OAuth login', () => {
  cy.visit('/login');
  cy.contains('Sign in with Google').click();
  // ❌ 跳到 accounts.google.com → cy.url() fail
  // 需要 cy.origin() 或 mock 整段 flow
});

// Playwright — 沒事
test('OAuth login', async ({ page }) => {
  await page.goto('/login');
  await page.click('text=Sign in with Google');
  await page.fill('input[type=email]', '[email protected]');
  await page.click('button:has-text("Next")');
  // ✓ 跨域沒問題
});

OAuth 工作流 = Playwright 絕對選

Component Testing — Cypress 還領先

// Cypress + React component test
import Button from './Button';

describe('Button', () => {
  it('renders text', () => {
    cy.mount(<Button>Click me</Button>);
    cy.contains('Click me').should('be.visible');
  });

  it('handles click', () => {
    const onClick = cy.stub();
    cy.mount(<Button onClick={onClick}>Click</Button>);
    cy.contains('Click').click();
    cy.wrap(onClick).should('have.been.calledOnce');
  });
});

這塊 Cypress UX 還是最好。但新案推 Vitest + Testing Library(更快、更輕量)。

平行測試 — Cypress 收費痛

flowchart LR
    PW[Playwright] -->|內建免費| PWS[--shard=N/4]
    PWS --> Free[$0]

    Cy[Cypress] --> CC{Cypress Cloud?}
    CC -->|是| Pay["$75 / month start"]
    CC -->|否| Slow[全部 serial 跑]

    style Free fill:#10b981,color:#fff
    style Pay fill:#ef4444,color:#fff
    style Slow fill:#f59e0b,color:#fff

Cypress 平行測試需要 Cypress Cloud 訂閱。150 個 case 跑 30 分鐘、付費後變 8 分鐘 — 但每月 $75 起跳。 Playwright --shard=1/4 免費就有

遷移 Cypress → Playwright 實戰

Step 1: 評估

flowchart TD
    Eval[評估] --> Q1{case 數 < 100?}
    Q1 -->|是| Big[全部重寫快]
    Q1 -->|否| Q2[漸進]

    Q2 --> S1[新 case 用 Playwright]
    S1 --> S2[舊 case 慢慢轉]
    S2 --> S3[Cypress 停止接受 new case]

    style Big fill:#10b981,color:#fff
    style S2 fill:#06b6d4,color:#fff

Step 2: API 對照表

Cypress Playwright
cy.visit(url) await page.goto(url)
cy.get(sel).click() await page.click(sel)
cy.get(sel).type(val) await page.fill(sel, val)
cy.contains(text) await page.getByText(text)
cy.url().should('include') await expect(page).toHaveURL()
cy.intercept('POST', url) await page.route(url)
cy.fixture('users.json') import users from './users.json'
cy.task('db:reset') 直接呼叫 helper function
beforeEach() test.beforeEach()

Step 3: codemod 工具

npx playwright-codemod-cypress ./cypress/e2e/

會幫你轉 80%、剩 20% 手工

Step 4: 跑舊新平行 1-2 週

確認 case behavior 一致、再下架 Cypress。

反模式 — Cypress 該換的訊號

flowchart TD
    Sig{該換訊號} --> S1["每天遇 cy.origin 卡關"]
    Sig --> S2["想測 Safari / iPad"]
    Sig --> S3["Cypress Cloud 月費 > $200"]
    Sig --> S4["跑 E2E > 30 分鐘"]
    Sig --> S5["平行常常 flaky"]
    Sig --> S6["招新人都會 Playwright 不會 Cypress"]
    Sig --> S7["AI workflow 工具都支援 Playwright"]

    style S1 fill:#ef4444,color:#fff
    style S2 fill:#ef4444,color:#fff
    style S3 fill:#ef4444,color:#fff
    style S4 fill:#ef4444,color:#fff
    style S5 fill:#ef4444,color:#fff
    style S6 fill:#ef4444,color:#fff
    style S7 fill:#ef4444,color:#fff

任 3 個 → 啟動遷移討論

面試常考題

Q1: Cypress 跟 Playwright 差別?

好答案(30 秒版):

Cypress 是 chainable API、time travel debugger 神級、Chrome-only 體驗最好。Playwright 是 async/await 標準、原生 cross-origin / multi-tab / 多瀏覽器 / 免費平行。新案推 Playwright、舊 Cypress 案不一定要遷。

Q2: 你會選哪個?

好答案

看情境。新案 90% 選 Playwright(多平台 + 免費平行)。Component testing 有時保留 Cypress。但團隊現在大多走 Playwright + Vitest 組合、Cypress 漸式微。

Q3: Cypress 如何處理 cross-origin?

好答案

cy.origin('https://other.com', () => { ... })、但仍有 cookie 限制與環境變數設置麻煩。Playwright 原生支援、寫起來像同 domain。

給仍在 Cypress 的人

不要急著遷。但:
1. 學 Playwright(找工作必備)
2. 新 case 開始用 Playwright
3. 舊 case 維持 Cypress 直到痛點累積
4. 痛了再 codemod 遷

兩個都會的 QA = 求職市場熱門。

最後

Cypress 是好工具、time travel UX 至今沒對手。但整體生態 + 跨平台支援 + 免費平行 → Playwright 全方位贏。2026 年的新案沒理由選 Cypress、但 5 年的 Cypress 案也不用急遷。會兩個 = 最強

延伸: - Playwright 入門 — 30 分鐘上手 - Page Object Model 實戰 - GitHub Actions × Playwright 完整實戰