DEV Community

Cover image for Cypress APIテスト: cy.request()でAPIをテストする方法
Akira
Akira

Posted on • Originally published at apidog.com

Cypress APIテスト: cy.request()でAPIをテストする方法

すでにCypressをエンドツーエンドテストで使っているなら、UIを操作せずにAPIを直接叩き、ステータスコードやJSONレスポンスを検証できます。cy.request() はブラウザではなくNodeプロセスから実際のHTTPリクエストを送るため、エンドポイント単体の検証や、UIテスト前の状態セットアップに使えます。

今すぐApidogを試す

この記事では、CypressでAPIをテストする実装手順を整理します。cy.request() の基本、レスポンスのアサート、認証トークンの再利用、cy.intercept() によるブラウザ通信のスタブ化、そしてCypressとAPIネイティブツールの使い分けを扱います。

CypressでAPIをテストする理由

Cypressの多くのテストはブラウザ操作を中心に作られます。ただし、UIで確認している挙動の多くはAPIに依存しています。

例:

  • /login がトークンを返す
  • /users が期待したスキーマで返る
  • POST /posts がレコードを作成する
  • エラー時にUIが適切な表示をする

APIを直接テストすると、次のメリットがあります。

  • UIレンダリングを待たないため、テストが速い
  • DOM要素の待機やクリック操作が不要
  • cy.request() でテストデータを素早く作成できる
  • UIテストの前提条件をコードで安定して準備できる

たとえば、サインアップフォームをクリックしてユーザーを作る代わりに、APIへ1回 POST してから本当に検証したい画面へ進めます。

すでにCypressを導入しているプロジェクトなら、新しいテストランナーを追加せずに、同じスペックファイル、同じChaiアサーション、同じCI実行の中でAPIチェックを追加できます。

cy.request() の基本

cy.request() はHTTPリクエストを送信し、レスポンスを返すCypressコマンドです。ブラウザではなくCypressのNodeプロセスから実行されるため、CORSや同一オリジンポリシーの制約を受けません。

主な呼び出し形式は次のとおりです。

cy.request(url)
cy.request(url, body)
cy.request(method, url)
cy.request(method, url, body)
cy.request(options)
Enter fullscreen mode Exit fullscreen mode

最小構成ではURLだけを渡します。メソッドを省略すると GET になります。

cy.request('https://jsonplaceholder.typicode.com/users')
Enter fullscreen mode Exit fullscreen mode

POST、ヘッダー、ボディ、クエリパラメータなどを指定したい場合は、オプションオブジェクトを使います。

cy.request({
  method: 'POST',
  url: 'https://jsonplaceholder.typicode.com/posts',
  headers: {
    'Content-Type': 'application/json'
  },
  body: {
    title: 'API test',
    body: 'created from Cypress',
    userId: 1
  }
})
Enter fullscreen mode Exit fullscreen mode

よく使うオプションは次のとおりです。

オプション 用途
method GETPOSTPUTPATCHDELETE など。デフォルトは GET
url リクエスト先エンドポイント
body リクエストボディ。オブジェクトはJSONとして送信される
headers リクエストヘッダー
auth HTTPベーシック認証
qs クエリ文字列パラメータ
failOnStatusCode false にすると4xx/5xxでもテストを即失敗させない

ネガティブテストでは failOnStatusCode: false が重要です。デフォルトでは、cy.request() は2xxまたは3xx以外のステータスで失敗します。

cy.request({
  method: 'GET',
  url: 'https://jsonplaceholder.typicode.com/users/99999',
  failOnStatusCode: false
}).then((response) => {
  expect(response.status).to.eq(404)
})
Enter fullscreen mode Exit fullscreen mode

ステータスとボディをアサートする

cy.request() はレスポンスオブジェクトを返します。.then() で受け取り、必要な値を検証します。

よく使うプロパティは次の4つです。

プロパティ 内容
status HTTPステータスコード
body レスポンスボディ。JSONの場合はJavaScriptオブジェクトとして扱える
headers レスポンスヘッダー
duration リクエストにかかった時間。ミリ秒単位

基本的なGET APIテストは次のように書けます。

describe('Users API', () => {
  it('returns a list of users', () => {
    cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
      expect(response.status).to.eq(200)
      expect(response.body).to.be.an('array')
      expect(response.body).to.have.length(10)
      expect(response.body[0]).to.have.property('email')
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

response.body はJSONとしてパース済みなので、通常のChaiアサーションをそのまま使えます。

例:

expect(response.body).to.be.an('array')
expect(response.body[0]).to.have.property('id')
expect(response.body[0].email).to.include('@')
Enter fullscreen mode Exit fullscreen mode

レスポンスヘッダーも検証できます。

cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
  expect(response.headers['content-type']).to.include('application/json')
})
Enter fullscreen mode Exit fullscreen mode

APIレスポンスの検証設計については、APIアサーションAPIテストのベストプラクティスも参考になります。

リクエストをチェインして認証トークンを再利用する

実際のAPIでは、ログインしてトークンを取得し、そのトークンを後続リクエストに渡すパターンがよくあります。

Cypressでは cy.request() をチェインして実装できます。

describe('Authenticated API flow', () => {
  it('logs in and fetches a protected resource', () => {
    cy.request({
      method: 'POST',
      url: 'https://api.example.com/login',
      body: {
        email: 'user@example.com',
        password: 'secret'
      }
    }).then((loginResponse) => {
      expect(loginResponse.status).to.eq(200)

      const token = loginResponse.body.token

      cy.request({
        method: 'GET',
        url: 'https://api.example.com/profile',
        headers: {
          Authorization: `Bearer ${token}`
        }
      }).then((profileResponse) => {
        expect(profileResponse.status).to.eq(200)
        expect(profileResponse.body).to.have.property('email', 'user@example.com')
      })
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

複数のテストで同じ認証処理を使う場合は、beforeEach に切り出します。

describe('Authenticated API tests', () => {
  beforeEach(() => {
    cy.request({
      method: 'POST',
      url: 'https://api.example.com/login',
      body: {
        email: 'user@example.com',
        password: 'secret'
      }
    }).then((response) => {
      Cypress.env('authToken', response.body.token)
    })
  })

  it('fetches profile', () => {
    cy.request({
      method: 'GET',
      url: 'https://api.example.com/profile',
      headers: {
        Authorization: `Bearer ${Cypress.env('authToken')}`
      }
    }).then((response) => {
      expect(response.status).to.eq(200)
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

この「ログインして、その結果を次のリクエストで使う」構成は、複数APIをまたぐAPI統合テストでもよく使うパターンです。

cy.intercept() でブラウザ通信をスタブ化する

cy.request() は実際のAPIへリクエストを送ります。一方、フロントエンドアプリがブラウザから送るリクエストを監視・差し替えたい場合は cy.intercept() を使います。

重要な違いは次のとおりです。

  • cy.request() はCypressのNodeプロセスからHTTPリクエストを送る
  • cy.intercept() はブラウザ上のアプリが送るリクエストを捕捉する
  • cy.intercept()cy.request() を捕捉しない

つまり、API自体をテストしたいなら cy.request()、UIがAPIレスポンスにどう反応するかをテストしたいなら cy.intercept() を使います。

固定レスポンスを返す例です。

cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [{ id: 1, name: 'Ada' }]
})
Enter fullscreen mode Exit fullscreen mode

これで、ブラウザから /api/users に送られるリクエストは、実際のバックエンドではなく上記の固定レスポンスを受け取ります。

これにより、次のようなケースを再現できます。

  • 空の一覧
  • 500 エラー
  • 遅いレスポンス
  • 特定フィールドが欠落したレスポンス
  • 権限エラー

リクエストが発生したことを確認するには、.as() でエイリアスを付けて cy.wait() します。

it('shows an error banner when the API fails', () => {
  cy.intercept('GET', '/api/users', {
    statusCode: 500,
    body: { message: 'Server error' }
  }).as('getUsers')

  cy.visit('/dashboard')

  cy.wait('@getUsers')
    .its('response.statusCode')
    .should('eq', 500)

  cy.contains('Something went wrong').should('be.visible')
})
Enter fullscreen mode Exit fullscreen mode

ポイントは、リクエストを発生させる操作より前に cy.intercept() を設定することです。後から設定すると捕捉できません。

レスポンスをスタブすべきか、サービス全体をモックすべきかを整理したい場合は、APIモックAPIスタブ vs APIモックも参考になります。

APIテストにCypressが適している場面

Cypressは、UIテストスイートを補助するAPIチェックに向いています。

たとえば次の用途です。

  • UIテスト前にテストデータを作成する
  • ログイン処理をAPIで済ませる
  • UIが依存するエンドポイントの最低限の疎通を確認する
  • エラー時のUI表示を cy.intercept() で検証する
  • 既存のCypress CIに軽量なAPIスモークテストを追加する

このようなケースでは、cy.request()cy.intercept() を使うことで、テストコードとCIを一箇所にまとめられます。

Cypressが適さない場面

大規模なスタンドアロンAPIテストスイートでは、Cypressだけで運用するのが不便になることがあります。

主な理由は次のとおりです。

  • 視覚的なリクエストビルダーがない
  • リクエスト、ヘッダー、ボディ、クエリをすべてコードで管理する必要がある
  • API専用CIでもCypressの実行コンテキストが必要になる
  • API定義、ドキュメント、テストをチームで共有しづらい
  • 数百のエンドポイントを扱うと、テストコードの保守コストが増える

APIテストが主作業になる場合は、APIネイティブツールの方が扱いやすくなります。

ApidogはAPI自体を中心に設計されたツールです。エンドポイントを定義し、リクエスト、環境、認証、テストシナリオを共有ワークスペースで管理できます。一般的なケースでは、視覚的なリクエストビルダーとアサーションを使ってテストを構築できます。

CIでは、Apidog CLIで保存済みのテストシナリオをヘッドレス実行できます。

npm install -g apidog-cli

apidog run \
  --access-token "$APIDOG_ACCESS_TOKEN" \
  -t <scenarioOrSuiteId> \
  -e <environmentId> \
  -r cli,html,junit
Enter fullscreen mode Exit fullscreen mode

各オプションの役割は次のとおりです。

オプション 内容
--access-token Apidogのアクセストークン
-t 実行するシナリオまたはスイートID
-e 使用する環境ID
-r レポート形式。clihtmljsonjunit など
-d / --iteration-data データファイルを使った反復実行
--upload-report 結果をワークスペースへアップロード

JUnit出力は、多くのCIダッシュボードにそのまま取り込めます。なお、Apidog CLIは保存済みシナリオを実行するためのものであり、インタラクティブなリクエスト送信ツールではありません。

判断基準はシンプルです。

  • ブラウザテストを支えるAPIチェックには cy.request()
  • UIレスポンス制御には cy.intercept()
  • APIテストが中心ならAPIネイティブツール

比較検討する場合は、CI/CDで実行されるAPIテスト自動化ツール30のベストAPIテストツールも参考になります。

FAQ

cy.request() はブラウザで実行されますか?

いいえ。cy.request() はブラウザの外、CypressのNodeプロセスから実行されます。そのため、CORSや同一オリジンポリシーの影響を受けません。また、cy.intercept()cy.request() を捕捉できない理由でもあります。

ブラウザが送るリクエストを検証したい場合は、アプリの操作でリクエストを発生させ、cy.intercept() で捕捉します。

テストを失敗させずに400や404を検証するには?

failOnStatusCode: false を指定します。

cy.request({
  method: 'GET',
  url: 'https://api.example.com/items/not-found',
  failOnStatusCode: false
}).then((response) => {
  expect(response.status).to.eq(404)
})
Enter fullscreen mode Exit fullscreen mode

デフォルトでは、cy.request() は2xxまたは3xx以外のステータスでテストを失敗させます。

UIテスト前に cy.request() でログインできますか?

はい。よく使われるパターンです。

ログインAPIへ POST し、response.body からトークンを取得して、Cypress.env()localStorage、またはクッキーに保存します。これにより、ログインフォーム操作を省略し、UIテストを認証済み状態から開始できます。

cy.request()cy.intercept() の違いは?

cy.request() は実際のHTTPリクエストを送信して、実際のレスポンスを取得します。API自体の検証に使います。

cy.intercept() は、フロントエンドがブラウザで送るリクエストを監視または差し替えます。UIが特定のAPIレスポンスにどう反応するかを確認するために使います。

APIテストにはCypressと専用ツールのどちらを使うべきですか?

既存のCypressブラウザテストを補助するAPIチェックならCypressで十分です。

一方で、大規模なAPIテストスイート、CI専用のAPIパイプライン、共有API定義、チームでのテスト管理が必要な場合は、ApidogのようなAPIネイティブツールが適しています。テスト設計の考え方は、APIテストのベストプラクティスも参考になります。

Top comments (0)