MeWrite Docs

Cypress: Timed out retrying: Expected to find element

Cypressで要素が見つからない場合のエラー

概要

CypressでDOM要素が見つからず、リトライしてもタイムアウトした場合に発生するエラーです。

エラーメッセージ

Timed out retrying after 4000ms: Expected to find element: `.submit-button`, but never found it.

原因

  1. セレクタの誤り: CSSセレクタが間違っている
  2. 要素が動的: 非同期でレンダリングされる
  3. 条件付きレンダリング: 特定条件でのみ表示
  4. iframeやShadow DOM: 通常のDOMツリー外

解決策

1. 正しいセレクタを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 悪い例:脆いセレクタ
cy.get('.MuiButton-root:nth-child(3)').click();

// 良い例:data-testid
cy.get('[data-testid="submit-button"]').click();

// 良い例:contains
cy.contains('button', 'Submit').click();

// 良い例:role
cy.get('[role="button"][aria-label="Save"]').click();

2. 自動リトライの理解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Cypressは自動的にリトライする(デフォルト4秒)
cy.get('[data-testid="item"]').should('have.length', 5);

// タイムアウトを延長
cy.get('[data-testid="loading"]', { timeout: 10000 })
  .should('not.exist');

// should は常にリトライする
cy.get('[data-testid="counter"]')
  .should('have.text', '10');

3. 存在確認

1
2
3
4
5
6
7
8
// 要素が存在するまで待機
cy.get('[data-testid="modal"]').should('exist');

// 要素が表示されるまで待機
cy.get('[data-testid="content"]').should('be.visible');

// 要素がなくなるまで待機
cy.get('[data-testid="spinner"]').should('not.exist');

4. ネットワークリクエストの待機

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// APIレスポンスを待機
cy.intercept('GET', '/api/users').as('getUsers');
cy.visit('/users');
cy.wait('@getUsers');
cy.get('[data-testid="user-list"]').should('be.visible');

// レスポンスのモック
cy.intercept('GET', '/api/users', {
  fixture: 'users.json'
}).as('getUsers');

5. 条件付きテスト

1
2
3
4
5
6
// 要素の存在に応じて処理
cy.get('body').then(($body) => {
  if ($body.find('[data-testid="cookie-banner"]').length > 0) {
    cy.get('[data-testid="accept-cookies"]').click();
  }
});

6. iframeの操作

1
2
3
4
5
6
7
// iframeの内部要素にアクセス
cy.get('iframe#my-frame')
  .its('0.contentDocument.body')
  .should('not.be.empty')
  .then(cy.wrap)
  .find('[data-testid="button"]')
  .click();

7. Shadow DOMの操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Shadow DOMを含む要素
cy.get('my-component')
  .shadow()
  .find('[data-testid="inner-button"]')
  .click();

// グローバル設定
// cypress.config.js
module.exports = {
  includeShadowDom: true
};

8. カスタムコマンド

1
2
3
4
5
6
7
// cypress/support/commands.js
Cypress.Commands.add('getByTestId', (testId) => {
  return cy.get(`[data-testid="${testId}"]`);
});

// 使用
cy.getByTestId('submit-button').click();

9. デバッグ

1
2
3
4
5
6
7
8
cy.get('[data-testid="element"]')
  .debug()  // ブラウザのDevToolsでデバッグ
  .click();

// スナップショットを確認
cy.get('[data-testid="element"]')
  .pause()  // テストを一時停止
  .click();

10. グローバル設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// cypress.config.js
module.exports = defineConfig({
  e2e: {
    defaultCommandTimeout: 10000,  // デフォルト4000ms
    pageLoadTimeout: 60000,
    retries: {
      runMode: 2,
      openMode: 0
    }
  }
});

よくある間違い

  • cy.get() の戻り値を変数に保存しようとする(Cypressはチェーン式)
  • setTimeout や async/await を使用(Cypressの自動リトライが無効になる)
  • .then() 内で cy.get() を使用(非推奨)
  • 要素の表示前にクリックしようとする

最終更新: 2025-12-09