10日で覚えるCypressDay 10: CI/CDとベストプラクティス
books.chapter 1010日で覚えるCypress

Day 10: CI/CDとベストプラクティス

今日学ぶこと

  • ヘッドレスモードでのテスト実行
  • GitHub Actionsでの設定
  • 並列テスト実行
  • Cypress Cloudの紹介
  • テストレポートの生成
  • パフォーマンス最適化
  • フレイキーテストへの対処
  • プロジェクトのディレクトリ構成
  • 10日間の振り返り

ヘッドレスモードでのテスト実行

Cypressにはテスト実行に2つのモードがあります。

flowchart LR
    subgraph Open["cypress open"]
        O1["ブラウザGUI表示"]
        O2["インタラクティブ"]
        O3["開発時に使用"]
    end

    subgraph Run["cypress run"]
        R1["ヘッドレス実行"]
        R2["自動実行"]
        R3["CI/CDで使用"]
    end

    style Open fill:#3b82f6,color:#fff
    style Run fill:#22c55e,color:#fff

基本コマンド

# ブラウザGUIを開いてテスト(開発時)
npx cypress open

# ヘッドレスモードで全テスト実行(CI/CD向け)
npx cypress run

# 特定のspecファイルのみ実行
npx cypress run --spec "cypress/e2e/login.cy.js"

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

# specパターンで複数ファイルを指定
npx cypress run --spec "cypress/e2e/auth/**/*.cy.js"

package.json でのスクリプト定義

{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run",
    "cy:run:chrome": "cypress run --browser chrome",
    "cy:run:auth": "cypress run --spec 'cypress/e2e/auth/**/*.cy.js'",
    "test:e2e": "start-server-and-test dev http://localhost:3000 cy:run"
  }
}

start-server-and-test パッケージを使うと、サーバー起動を待ってからテストを実行できます。

npm install --save-dev start-server-and-test

GitHub Actionsでの設定

基本的なワークフロー

# .github/workflows/cypress.yml
name: Cypress Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  cypress-run:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cypress run
        uses: cypress-io/github-action@v6
        with:
          build: npm run build
          start: npm start
          wait-on: 'http://localhost:3000'
          wait-on-timeout: 120

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: cypress-screenshots
          path: cypress/screenshots

      - name: Upload videos
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: cypress-videos
          path: cypress/videos

ワークフローの流れ

flowchart TB
    subgraph CI["GitHub Actions ワークフロー"]
        A["コードをチェックアウト"] --> B["Node.jsセットアップ"]
        B --> C["依存関係インストール"]
        C --> D["アプリケーションビルド"]
        D --> E["サーバー起動"]
        E --> F["Cypressテスト実行"]
        F --> G{"成功?"}
        G -->|"Yes"| H["完了"]
        G -->|"No"| I["スクリーンショット保存"]
        I --> J["失敗を報告"]
    end

    style CI fill:#8b5cf6,color:#fff
    style H fill:#22c55e,color:#fff
    style J fill:#ef4444,color:#fff

公式GitHub Action の主要オプション

オプション 説明
build ビルドコマンド npm run build
start サーバー起動コマンド npm start
wait-on 待機するURL http://localhost:3000
browser ブラウザ指定 chrome
spec テストファイル指定 cypress/e2e/**/*.cy.js
record Cypress Cloudに記録 true
parallel 並列実行 true

並列テスト実行

テスト数が増えると実行時間が長くなります。並列実行で大幅に短縮できます。

flowchart TB
    subgraph Sequential["直列実行: 30分"]
        S1["テスト群A: 10分"] --> S2["テスト群B: 10分"] --> S3["テスト群C: 10分"]
    end

    subgraph Parallel["並列実行: 10分"]
        P1["マシン1: テスト群A 10分"]
        P2["マシン2: テスト群B 10分"]
        P3["マシン3: テスト群C 10分"]
    end

    style Sequential fill:#f59e0b,color:#000
    style Parallel fill:#22c55e,color:#fff

GitHub Actionsでの並列実行

# .github/workflows/cypress-parallel.yml
name: Cypress Parallel Tests

on: [push]

jobs:
  cypress-run:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        containers: [1, 2, 3]  # 3台のマシンで並列実行

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Cypress run
        uses: cypress-io/github-action@v6
        with:
          start: npm start
          wait-on: 'http://localhost:3000'
          record: true
          parallel: true
          group: 'CI Parallel'
        env:
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

並列実行にはCypress Cloudのレコードキーが必要です。


Cypress Cloudの紹介

Cypress Cloud(旧 Cypress Dashboard)は、テスト結果の管理・分析を行うクラウドサービスです。

主な機能

機能 説明
テスト記録 実行結果、スクリーンショット、ビデオをクラウドに保存
並列実行の最適化 テスト時間に基づく自動分散
フレイキーテスト検出 不安定なテストを自動で特定
分析ダッシュボード 成功率、実行時間のトレンド分析
GitHubステータスチェック PRにテスト結果を表示

セットアップ

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  projectId: 'your-project-id',  // Cypress Cloudで取得
  e2e: {
    // ...
  },
});
# テスト結果を記録
npx cypress run --record --key YOUR_RECORD_KEY

テストレポートの生成

Mochawesome レポーター

視覚的に美しいHTMLレポートを生成できます。

npm install --save-dev mochawesome mochawesome-merge mochawesome-report-generator
// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    reporter: 'mochawesome',
    reporterOptions: {
      reportDir: 'cypress/reports',
      overwrite: false,
      html: false,
      json: true,
    },
  },
});

レポートの生成スクリプト

// package.json
{
  "scripts": {
    "cy:run": "cypress run",
    "report:merge": "mochawesome-merge cypress/reports/*.json > cypress/reports/merged.json",
    "report:generate": "marge cypress/reports/merged.json --reportDir cypress/reports/html",
    "test:report": "npm run cy:run && npm run report:merge && npm run report:generate"
  }
}
# テスト実行 → レポート生成
npm run test:report

JUnit レポーター(CI/CD向け)

JUnit形式はJenkins、GitLab CI、CircleCIなど多くのCIツールで対応しています。

npm install --save-dev cypress-multi-reporters mocha-junit-reporter
// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    reporter: 'cypress-multi-reporters',
    reporterOptions: {
      configFile: 'reporter-config.json',
    },
  },
});
// reporter-config.json
{
  "reporterEnabled": "mochawesome, mocha-junit-reporter",
  "mochawesomeReporterOptions": {
    "reportDir": "cypress/reports/mochawesome",
    "overwrite": false,
    "html": false,
    "json": true
  },
  "mochaJunitReporterReporterOptions": {
    "mochaFile": "cypress/reports/junit/results-[hash].xml"
  }
}

パフォーマンス最適化

テスト速度を改善するテクニック

flowchart TB
    subgraph Optimization["速度改善テクニック"]
        A["APIでのセットアップ"]
        B["不要な待機の排除"]
        C["セッション管理"]
        D["並列実行"]
        E["選択的テスト実行"]
    end

    A --> F["UIログインをスキップ"]
    B --> G["cy.wait()の削減"]
    C --> H["cy.session()の活用"]
    D --> I["複数マシンで実行"]
    E --> J["変更に関連するテストのみ"]

    style Optimization fill:#f59e0b,color:#000

1. APIを使ったセットアップ

// 遅い: UIでログイン(5秒)
beforeEach(() => {
  cy.visit('/login');
  cy.get('#email').type('test@example.com');
  cy.get('#password').type('password');
  cy.get('#submit').click();
  cy.url().should('include', '/dashboard');
});

// 速い: APIでログイン(0.5秒)
beforeEach(() => {
  cy.request('POST', '/api/login', {
    email: 'test@example.com',
    password: 'password',
  }).then((response) => {
    window.localStorage.setItem('token', response.body.token);
  });
  cy.visit('/dashboard');
});

2. cy.session() でセッションをキャッシュ

// セッションをキャッシュして再利用
Cypress.Commands.add('login', (email, password) => {
  cy.session([email, password], () => {
    cy.request('POST', '/api/login', { email, password })
      .then((response) => {
        window.localStorage.setItem('token', response.body.token);
      });
  });
});

// テストで使用
beforeEach(() => {
  cy.login('test@example.com', 'password');
  cy.visit('/dashboard');
});

cy.session() は同じ引数で2回目以降の呼び出し時にキャッシュを再利用します。

3. 不要な待機の排除

// NG: 固定時間の待機
cy.wait(3000);
cy.get('.result').should('be.visible');

// OK: アサーションベースの待機
cy.get('.result', { timeout: 10000 }).should('be.visible');

// OK: ネットワークリクエストの待機
cy.intercept('GET', '/api/data').as('getData');
cy.get('#load-btn').click();
cy.wait('@getData');
cy.get('.result').should('be.visible');

速度改善の効果比較

テクニック 改善前 改善後 効果
APIログイン 5秒/テスト 0.5秒/テスト 90%短縮
cy.session() 0.5秒/テスト 0.05秒/テスト 90%短縮
cy.wait()排除 3秒固定待機 0.1〜3秒 平均50%短縮
並列実行(3台) 30分 10分 66%短縮

フレイキーテストへの対処

フレイキーテスト(Flaky Test)とは、同じコードでも成功したり失敗したりする不安定なテストです。

flowchart TB
    subgraph Causes["フレイキーの原因"]
        C1["タイミング依存"]
        C2["テスト間の依存"]
        C3["外部サービス依存"]
        C4["アニメーション"]
        C5["動的データ"]
    end

    subgraph Solutions["対策"]
        S1["アサーションベース待機"]
        S2["テストの独立性確保"]
        S3["cy.intercept()でモック"]
        S4["アニメーション無効化"]
        S5["固定テストデータ使用"]
    end

    C1 --> S1
    C2 --> S2
    C3 --> S3
    C4 --> S4
    C5 --> S5

    style Causes fill:#ef4444,color:#fff
    style Solutions fill:#22c55e,color:#fff

1. アニメーションの無効化

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    // テスト時にCSSアニメーションを無効化
    setupNodeEvents(on, config) {
      // ...
    },
  },
});
// cypress/support/e2e.js
// テスト時にアニメーションを無効化するCSS
Cypress.on('window:before:load', (win) => {
  const style = win.document.createElement('style');
  style.innerHTML = `
    *, *::before, *::after {
      transition-duration: 0s !important;
      animation-duration: 0s !important;
    }
  `;
  win.document.head.appendChild(style);
});

2. 外部APIのモック

// 外部APIに依存しない安定したテスト
beforeEach(() => {
  cy.intercept('GET', '/api/weather', {
    statusCode: 200,
    body: { temp: 25, condition: 'sunny' },
  }).as('getWeather');
});

it('天気情報を表示する', () => {
  cy.visit('/dashboard');
  cy.wait('@getWeather');
  cy.get('.weather').should('contain', '25');
});

3. フレイキーテスト検出のチェックリスト

チェック項目 確認内容
cy.wait(ms) の使用 固定待機を使っていないか
テスト順序依存 単独で実行しても成功するか
外部API呼び出し モックを使っているか
日付/時刻依存 cy.clock() を使っているか
ランダムデータ 固定のテストデータを使っているか

プロジェクトのディレクトリ構成

推奨構成

project-root/
├── cypress/
│   ├── e2e/                    # E2Eテストファイル
│   │   ├── auth/               # 認証関連
│   │   │   ├── login.cy.js
│   │   │   ├── logout.cy.js
│   │   │   └── register.cy.js
│   │   ├── dashboard/          # ダッシュボード
│   │   │   ├── overview.cy.js
│   │   │   └── settings.cy.js
│   │   └── products/           # 商品管理
│   │       ├── list.cy.js
│   │       ├── detail.cy.js
│   │       └── cart.cy.js
│   ├── fixtures/               # テストデータ(JSON)
│   │   ├── users.json
│   │   ├── products.json
│   │   └── api-responses/
│   │       ├── login-success.json
│   │       └── login-error.json
│   ├── pages/                  # Page Objects
│   │   ├── LoginPage.js
│   │   ├── DashboardPage.js
│   │   └── ProductPage.js
│   ├── support/                # サポートファイル
│   │   ├── commands.js         # カスタムコマンド
│   │   └── e2e.js             # グローバル設定
│   ├── downloads/              # ダウンロードテスト用
│   └── reports/                # テストレポート
├── cypress.config.js           # Cypress設定ファイル
├── .github/
│   └── workflows/
│       └── cypress.yml         # CI/CD設定
└── package.json

構成のポイント

ディレクトリ 役割 ポイント
e2e/ テストファイル 機能ごとにフォルダ分け
fixtures/ テストデータ JSON形式で管理
pages/ Page Objects ページごとにファイル作成
support/ 共通処理 カスタムコマンド、グローバル設定

10日間の学習の振り返り

flowchart TB
    subgraph Journey["10日間の学習の道のり"]
        D1["Day 1<br/>Cypressとは"]
        D2["Day 2<br/>セットアップ"]
        D3["Day 3<br/>基本コマンド"]
        D4["Day 4<br/>アサーション"]
        D5["Day 5<br/>フォーム操作"]
        D6["Day 6<br/>ネットワーク"]
        D7["Day 7<br/>カスタムコマンド"]
        D8["Day 8<br/>Fixture & データ"]
        D9["Day 9<br/>デバッグ・戦略"]
        D10["Day 10<br/>CI/CD"]

        D1 --> D2 --> D3 --> D4 --> D5
        D5 --> D6 --> D7 --> D8 --> D9 --> D10
    end

    style D1 fill:#3b82f6,color:#fff
    style D2 fill:#3b82f6,color:#fff
    style D3 fill:#8b5cf6,color:#fff
    style D4 fill:#8b5cf6,color:#fff
    style D5 fill:#8b5cf6,color:#fff
    style D6 fill:#f59e0b,color:#000
    style D7 fill:#f59e0b,color:#000
    style D8 fill:#f59e0b,color:#000
    style D9 fill:#22c55e,color:#fff
    style D10 fill:#22c55e,color:#fff
Day テーマ 学んだこと
1 Cypressとは E2Eテストの概要、Cypressの特徴
2 セットアップ インストール、設定、最初のテスト
3 基本コマンド visit, get, click, type, should
4 アサーション should, expect, assert の使い分け
5 フォーム操作 入力、選択、バリデーションテスト
6 ネットワーク intercept, wait, APIモック
7 カスタムコマンド 再利用可能なコマンドの作成
8 Fixture & データ テストデータ管理、fixture の活用
9 デバッグ・戦略 デバッグツール、Page Object、テスト設計
10 CI/CD 自動テスト、並列実行、ベストプラクティス

次のステップ

Cypressの基礎を身につけた今、さらに発展的なトピックに挑戦しましょう。

Component Testing

Cypress 12以降、コンポーネントテストが公式サポートされています。

// React コンポーネントのテスト例
import { mount } from 'cypress/react';
import Button from './Button';

describe('Button', () => {
  it('クリックイベントが発火する', () => {
    const onClick = cy.stub().as('onClick');
    mount(<Button onClick={onClick}>Click me</Button>);
    cy.get('button').click();
    cy.get('@onClick').should('have.been.calledOnce');
  });
});

Visual Testing

画面のスクリーンショットを比較して、意図しないUI変更を検出します。

npm install --save-dev cypress-visual-regression
it('ログインページのレイアウトが正しい', () => {
  cy.visit('/login');
  cy.compareSnapshot('login-page', 0.1);  // 0.1%の差異まで許容
});

Accessibility Testing

npm install --save-dev cypress-axe
import 'cypress-axe';

it('アクセシビリティ違反がない', () => {
  cy.visit('/');
  cy.injectAxe();
  cy.checkA11y();
});

まとめ

概念 説明
cypress run ヘッドレスモードでのテスト実行
GitHub Actions CI/CDでの自動テスト実行
並列実行 複数マシンでテストを分散実行
Cypress Cloud テスト結果の記録・分析サービス
Mochawesome HTMLテストレポートの生成
cy.session() セッションキャッシュで高速化
フレイキーテスト 不安定なテストの検出と対策
ディレクトリ構成 機能別フォルダ分けによる整理

重要ポイント

  1. CI/CDでテストを自動化して品質を守る
  2. APIセットアップと**cy.session()**で速度を改善
  3. フレイキーテストは放置せず根本原因を解決
  4. 並列実行で大規模テストスイートも効率的に
  5. レポートでテスト結果を可視化・共有する

練習問題

基本

  1. cypress run コマンドでテストをヘッドレス実行し、ビデオが生成されることを確認してください。
  2. package.json にテスト実行のスクリプトを定義してください(cy:open, cy:run, test:e2e)。
  3. cypress.config.js でMochawesomeレポーターを設定してください。

応用

  1. GitHub Actionsのワークフローファイルを作成し、プッシュ時に自動でCypressテストが実行されるようにしてください。
  2. cy.session() を使って、ログイン処理をキャッシュするカスタムコマンドを作成してください。
  3. 外部APIのレスポンスを cy.intercept() でモックし、フレイキーにならないテストを書いてください。

チャレンジ

  1. 3台の並列マシンでテストを実行するGitHub Actionsワークフローを設計してください。テスト結果のスクリーンショットとビデオをアーティファクトとしてアップロードする設定も含めてください。

参考リンク


おめでとうございます!

10日間のCypress学習が完了しました!

学んだこと

  1. Day 1: Cypressとは何か、E2Eテストの重要性
  2. Day 2: 環境構築と最初のテスト
  3. Day 3: 基本コマンド(visit, get, click, type)
  4. Day 4: アサーションでテストを検証
  5. Day 5: フォーム操作とバリデーション
  6. Day 6: ネットワークリクエストのテスト
  7. Day 7: カスタムコマンドで再利用性向上
  8. Day 8: Fixtureとテストデータ管理
  9. Day 9: デバッグ手法とテスト戦略
  10. Day 10: CI/CDとベストプラクティス

これからの道

あなたはもうCypressの基礎をしっかりと身につけました。ここからは実際のプロジェクトでテストを書き、経験を積んでいくことが最も大切です。

テストを書くことは、ソフトウェアの品質を守るだけでなく、自信を持ってコードを変更し、素早くリリースすることを可能にします。今日学んだCI/CDの知識を活かして、チームの開発フローにCypressを組み込んでいきましょう。

テストの旅はここから始まります。学び続けて、信頼性の高いWebアプリケーションを作りましょう!