cypress の使い方

Cypress is a next generation front end testing tool built for the modern web

v15.13.0/週MITテスト
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Cypress の使い方完全ガイド — モダンWebのためのE2Eテストツール

一言でいうと

Cypressは、ブラウザ上で動作するE2E(エンドツーエンド)テスト・コンポーネントテストのためのオールインワンテストフレームワークです。セットアップの容易さ、リアルタイムリロード、タイムトラベルデバッグなど、開発者体験を最優先に設計されています。


どんな時に使う?

  1. Webアプリケーションの E2E テスト — ユーザーがブラウザで行う操作(ログイン、フォーム送信、ページ遷移など)を自動化し、アプリ全体の動作を検証したいとき
  2. コンポーネント単体テスト — React、Vue、Angular などのコンポーネントを実際のブラウザ環境でマウントしてテストしたいとき
  3. API モック・スタブを使った統合テスト — バックエンドのレスポンスをインターセプトし、フロントエンドの振る舞いを様々な条件下で検証したいとき

インストール

# npm
npm install --save-dev cypress

# yarn
yarn add --dev cypress

# pnpm
pnpm add --save-dev cypress

インストール後、初回起動でプロジェクトの雛形が自動生成されます。

npx cypress open

基本的な使い方

プロジェクト構成

初回起動後、以下のようなディレクトリ構成が生成されます。

cypress/
├── e2e/            # E2Eテストファイル
├── fixtures/       # テストデータ(JSONなど)
├── support/
│   ├── commands.ts # カスタムコマンド定義
│   └── e2e.ts      # E2Eテスト共通設定
└── tsconfig.json
cypress.config.ts     # Cypress設定ファイル

設定ファイル(cypress.config.ts)

import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    specPattern: 'cypress/e2e/**/*.cy.ts',
    supportFile: 'cypress/support/e2e.ts',
    viewportWidth: 1280,
    viewportHeight: 720,
    setupNodeEvents(on, config) {
      // Node.jsイベントリスナーをここに登録
    },
  },
  component: {
    devServer: {
      framework: 'react',
      bundler: 'vite',
    },
  },
});

最初のE2Eテスト

// cypress/e2e/login.cy.ts
describe('ログイン機能', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('正しい認証情報でログインできる', () => {
    cy.get('[data-testid="email-input"]').type('user@example.com');
    cy.get('[data-testid="password-input"]').type('password123');
    cy.get('[data-testid="login-button"]').click();

    cy.url().should('include', '/dashboard');
    cy.get('[data-testid="welcome-message"]').should(
      'contain',
      'ようこそ'
    );
  });

  it('誤ったパスワードでエラーが表示される', () => {
    cy.get('[data-testid="email-input"]').type('user@example.com');
    cy.get('[data-testid="password-input"]').type('wrongpassword');
    cy.get('[data-testid="login-button"]').click();

    cy.get('[data-testid="error-message"]')
      .should('be.visible')
      .and('contain', '認証に失敗しました');
  });
});

テストの実行

# GUIモード(インタラクティブ)
npx cypress open

# CLIモード(CI向け・ヘッドレス)
npx cypress run

# 特定のスペックファイルのみ実行
npx cypress run --spec "cypress/e2e/login.cy.ts"

# 特定のブラウザで実行
npx cypress run --browser chrome

よく使うAPI

1. cy.visit() — ページ遷移

// 基本
cy.visit('/about');

// baseUrlを使わず絶対URLで指定
cy.visit('https://example.com/about');

// オプション付き
cy.visit('/login', {
  timeout: 10000,
  headers: {
    'Accept-Language': 'ja',
  },
});

2. cy.get() / cy.contains() — 要素の取得

// CSSセレクタで取得
cy.get('.submit-button').click();

// data-testid属性で取得(推奨パターン)
cy.get('[data-testid="user-name"]').should('have.text', '田中太郎');

// テキスト内容で要素を検索
cy.contains('送信する').click();

// スコープを絞って検索
cy.get('[data-testid="user-list"]').contains('田中太郎').click();

// find で子要素を検索
cy.get('[data-testid="nav"]').find('a').should('have.length', 5);

3. cy.intercept() — ネットワークリクエストのインターセプト

// GETリクエストをモック
cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [
    { id: 1, name: '田中太郎' },
    { id: 2, name: '鈴木花子' },
  ],
}).as('getUsers');

cy.visit('/users');
cy.wait('@getUsers');
cy.get('[data-testid="user-item"]').should('have.length', 2);

// エラーレスポンスをシミュレート
cy.intercept('POST', '/api/orders', {
  statusCode: 500,
  body: { message: 'Internal Server Error' },
}).as('createOrder');

// リクエストの内容を検証
cy.intercept('POST', '/api/users', (req) => {
  expect(req.body).to.have.property('email', 'new@example.com');
  req.reply({ statusCode: 201, body: { id: 3, ...req.body } });
}).as('createUser');

4. cy.should() / cy.and() — アサーション

// 要素の状態を検証
cy.get('[data-testid="modal"]').should('be.visible');
cy.get('[data-testid="submit-btn"]').should('be.disabled');
cy.get('[data-testid="item-list"]').should('have.length', 3);

// チェーンで複数のアサーション
cy.get('[data-testid="status-badge"]')
  .should('be.visible')
  .and('have.class', 'active')
  .and('contain', '有効');

// コールバック形式(複雑な検証)
cy.get('[data-testid="price"]').should(($el) => {
  const price = parseInt($el.text().replace(/[^0-9]/g, ''), 10);
  expect(price).to.be.greaterThan(0);
  expect(price).to.be.lessThan(100000);
});

// URL・Cookieの検証
cy.url().should('include', '/dashboard');
cy.getCookie('session_id').should('exist');

5. cy.fixture() / カスタムコマンド — テストデータとコマンドの再利用

// cypress/fixtures/user.json
{
  "email": "test@example.com",
  "password": "securePassword123",
  "name": "テストユーザー"
}
// fixtureの利用
cy.fixture('user').then((user) => {
  cy.get('[data-testid="email-input"]').type(user.email);
  cy.get('[data-testid="password-input"]').type(user.password);
});

// fixtureをinterceptと組み合わせる
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as(
  'getProducts'
);
// cypress/support/commands.ts — カスタムコマンド定義
declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>;
    }
  }
}

Cypress.Commands.add('login', (email: string, password: string) => {
  // UIを経由せずAPIで直接ログイン(テスト高速化)
  cy.request('POST', '/api/auth/login', { email, password }).then(
    (response) => {
      window.localStorage.setItem('token', response.body.token);
    }
  );
});
// テストでの利用
describe('ダッシュボード', () => {
  beforeEach(() => {
    cy.login('admin@example.com', 'admin123');
    cy.visit('/dashboard');
  });

  it('ユーザー情報が表示される', () => {
    cy.get('[data-testid="user-profile"]').should('be.visible');
  });
});

類似パッケージとの比較

特徴CypressPlaywrightSelenium
対応ブラウザChrome, Firefox, Edge, ElectronChromium, Firefox, WebKitほぼ全ブラウザ
言語JavaScript / TypeScriptJS/TS, Python, Java, C# 等多言語対応
実行速度◎ 高速◎ 高速△ やや遅い
セットアップ容易さ◎ 非常に簡単○ 簡単△ 設定が多い
デバッグ体験◎ タイムトラベル・スナップショット○ トレースビューア△ 限定的
コンポーネントテスト○ 対応○ 対応× 非対応
マルチタブ操作× 非対応◎ 対応○ 対応
iframe操作△ 制限あり◎ 対応○ 対応
並列実行有料(Cypress Cloud)◎ 標準対応○ Grid利用
ライセンスMITApache 2.0Apache 2.0

選定の目安:

  • Cypress — フロントエンド開発者が手軽にE2Eテストを始めたい場合。DXが最も優れている
  • Playwright — マルチブラウザ・マルチタブ・複雑なシナリオが必要な場合。CIでの並列実行も標準で可能
  • Selenium — レガシーシステムや特殊なブラウザ環境が必要な場合

注意点・Tips

1. cy.wait() に固定時間を使わない

// ❌ アンチパターン — フレーキーテストの原因
cy.wait(3000);
cy.get('[data-testid="result"]').should('exist');

// ✅ ネットワークリクエストの完了を待つ
cy.intercept('GET', '/api/data').as('getData');
cy.wait('@getData');
cy.get('[data-testid="result"]').should('exist');

// ✅ should() は自動リトライされるので、要素の出現を待てる
cy.get('[data-testid="result"]', { timeout: 10000 }).should('be.visible');

2. テストの独立性を保つ

// ❌ テスト間で状態を共有する
let userId: string;

it('ユーザーを作成する', () => {
  // userId に代入...
});

it('ユーザーを削除する', () => {
  // 上のテストに依存している
});

// ✅ 各テストで必要な状態をセットアップする
beforeEach(() => {
  cy.request('POST', '/api/test/reset'); // テストDBリセット
  cy.login('admin@example.com', 'admin123');
});

3. セレクタは data-testid を使う

CSSクラスやタグ構造に依存するセレクタは、UIリファクタリングで壊れやすくなります。

// ❌ 壊れやすい
cy.get('.btn.btn-primary.mt-4').click();
cy.get('div > form > div:nth-child(2) > input').type('hello');

// ✅ 安定する
cy.get('[data-testid="submit-button"]').click();
cy.get('[data-testid="search-input"]').type('hello');

4. 環境変数の活用

// cypress.config.ts
export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    env: {
      apiUrl: 'http://localhost:8080',
      testUserEmail: 'test@example.com',
    },
  },
});
// テスト内で参照
cy.request(`${Cypress.env('apiUrl')}/api/health`);

// CLI から上書き
// npx cypress run --env apiUrl=https://staging.example.com

5. CI環境での実行

# GitHub Actions の例
name: E2E Tests
on: [push]
jobs:
  cypress:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npm start &
      - run: npx cypress run

Tips: 公式の `cypress

比較記事