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() | セッションキャッシュで高速化 |
| フレイキーテスト | 不安定なテストの検出と対策 |
| ディレクトリ構成 | 機能別フォルダ分けによる整理 |
重要ポイント
- CI/CDでテストを自動化して品質を守る
- APIセットアップと**cy.session()**で速度を改善
- フレイキーテストは放置せず根本原因を解決
- 並列実行で大規模テストスイートも効率的に
- レポートでテスト結果を可視化・共有する
練習問題
基本
cypress runコマンドでテストをヘッドレス実行し、ビデオが生成されることを確認してください。package.jsonにテスト実行のスクリプトを定義してください(cy:open,cy:run,test:e2e)。cypress.config.jsでMochawesomeレポーターを設定してください。
応用
- GitHub Actionsのワークフローファイルを作成し、プッシュ時に自動でCypressテストが実行されるようにしてください。
cy.session()を使って、ログイン処理をキャッシュするカスタムコマンドを作成してください。- 外部APIのレスポンスを
cy.intercept()でモックし、フレイキーにならないテストを書いてください。
チャレンジ
- 3台の並列マシンでテストを実行するGitHub Actionsワークフローを設計してください。テスト結果のスクリーンショットとビデオをアーティファクトとしてアップロードする設定も含めてください。
参考リンク
- Cypress CI/CD Introduction
- Cypress GitHub Actions
- Cypress Cloud
- Cypress Component Testing
- Mochawesome Reporter
おめでとうございます!
10日間のCypress学習が完了しました!
学んだこと
- Day 1: Cypressとは何か、E2Eテストの重要性
- Day 2: 環境構築と最初のテスト
- Day 3: 基本コマンド(visit, get, click, type)
- Day 4: アサーションでテストを検証
- Day 5: フォーム操作とバリデーション
- Day 6: ネットワークリクエストのテスト
- Day 7: カスタムコマンドで再利用性向上
- Day 8: Fixtureとテストデータ管理
- Day 9: デバッグ手法とテスト戦略
- Day 10: CI/CDとベストプラクティス
これからの道
あなたはもうCypressの基礎をしっかりと身につけました。ここからは実際のプロジェクトでテストを書き、経験を積んでいくことが最も大切です。
テストを書くことは、ソフトウェアの品質を守るだけでなく、自信を持ってコードを変更し、素早くリリースすることを可能にします。今日学んだCI/CDの知識を活かして、チームの開発フローにCypressを組み込んでいきましょう。
テストの旅はここから始まります。学び続けて、信頼性の高いWebアプリケーションを作りましょう!