はじめに
「昨日まで通っていたE2Eテストが、今日は理由もなく落ちる」——SeleniumやWebDriver系のツールでE2Eテストを書いたことがある人なら、一度は経験したことがあるのではないでしょうか。要素が見つかるまでsleepを挟んだり、タイムアウトの値を調整したりと、テストコードそのものよりも「テストを安定させるための工夫」に時間を取られてしまう。これではテストを書くモチベーションも下がってしまいます。
こうした悩みを根本から解決してくれるのが、ブラウザテストの定番ツールCypressです。ブラウザの中で直接動作する独自のアーキテクチャにより、待機処理を自動化し、テストの再現性を大きく高めてくれます。この記事では、Cypressの特徴から実践的な使い方までを解説します。
Cypressとは
Cypressは、ブラウザ上で動作するあらゆるアプリケーションをテストできるJavaScript製のテストフレームワークです。従来のWebDriverベースのツールとは異なり、テストコードをブラウザの内部で直接実行する独自の仕組みを採用しており、DOMの状態を正確に把握しながらテストを進められるのが最大の特徴です。E2Eテストだけでなく、コンポーネント単位のテストにも対応しています。
主な特徴
- 自動待機(Automatic Waiting) - 要素の出現やAPIレスポンスを自動的に待ってくれるため、
sleepや手動のタイムアウト設定が不要 - タイムトラベル機能 - テスト実行中の各ステップのスナップショットが記録され、失敗箇所をブラウザ上で時間を遡って確認できる
- リアルタイムリロード - コードを保存すると同時にテストが自動的に再実行され、開発中のフィードバックループが速い
- コンポーネントテスト対応 - React・Vue・Angularなどのコンポーネントを、実際のブラウザ環境で単体テストできる
インストール
npmまたはyarnでインストールできます。
npm install cypress --save-dev
yarn add cypress --dev
インストール後、以下のコマンドでCypressを起動すると、初回セットアップ用のGUIが開きます。
npx cypress open
基本的な使い方
まずはシンプルなE2Eテストを書いてみましょう。cypress/e2eディレクトリにテストファイルを作成します。
// cypress/e2e/login.cy.js
describe('ログイン機能', () => {
it('正しい情報でログインできる', () => {
cy.visit('/login')
cy.get('input[name="email"]').type('user@example.com')
cy.get('input[name="password"]').type('password123')
cy.get('button[type="submit"]').click()
cy.url().should('include', '/dashboard')
cy.contains('ようこそ').should('be.visible')
})
it('不正な情報の場合はエラーメッセージが表示される', () => {
cy.visit('/login')
cy.get('input[name="email"]').type('invalid@example.com')
cy.get('input[name="password"]').type('wrongpassword')
cy.get('button[type="submit"]').click()
cy.contains('メールアドレスまたはパスワードが正しくありません').should('be.visible')
})
})
cy.get()で要素を取得し、cy.get().type()やcy.get().click()のようにチェーン形式で操作を記述するだけで、要素の出現待ちは自動的に行われます。テストはターミナルからヘッドレスで実行することも可能です。
npx cypress run
実践的なユースケース
APIレスポンスをモックして、バックエンドの状態に依存しないテストを書くこともできます。cy.intercept()を使うと、特定のリクエストを傍受してレスポンスを差し替えられます。
// cypress/e2e/products.cy.js
describe('商品一覧ページ', () => {
it('商品が正しく表示される', () => {
cy.intercept('GET', '/api/products', {
statusCode: 200,
body: [
{ id: 1, name: 'ノートPC', price: 89800 },
{ id: 2, name: 'ワイヤレスマウス', price: 2980 },
],
}).as('getProducts')
cy.visit('/products')
cy.wait('@getProducts')
cy.get('[data-testid="product-item"]').should('have.length', 2)
cy.contains('ノートPC').should('be.visible')
})
it('APIエラー時はエラーメッセージを表示する', () => {
cy.intercept('GET', '/api/products', { statusCode: 500 }).as('getProductsError')
cy.visit('/products')
cy.wait('@getProductsError')
cy.contains('商品の取得に失敗しました').should('be.visible')
})
})
cy.intercept()でネットワークをコントロールできるため、外部APIの不安定さやレート制限に左右されず、常に同じ条件でUIの振る舞いを検証できます。CI環境では、こうしたモック戦略を組み合わせることでテストの実行時間と安定性を両立させやすくなります。
まとめ
Cypressは、自動待機の仕組みとブラウザ内で直接動作するアーキテクチャによって、E2Eテストにありがちな「不安定さ」を大きく軽減してくれるツールです。タイムトラベル機能で失敗の原因を素早く特定でき、cy.intercept()によるネットワーク制御で外部要因に左右されないテストも書けます。
まずは小さな1本のログインテストから始めて、徐々にカバレッジを広げていくのがおすすめです。慣れてきたら、コンポーネントテストやCI連携にも挑戦してみてください。