Cypressテストの安定性向上!cy.get() の一般的なエラーと解決策
Cypressのcy.get()
コマンドは、Webページ上のDOM要素(HTML要素)を取得するために最も頻繁に使用されるコマンドの一つです。JavaScriptのdocument.querySelector()
やjQueryの$()
に似ていますが、Cypressのテスト環境に特化した便利な機能や、テストの安定性を高めるための組み込み機能が追加されています。
cy.get()
の基本的な使い方
cy.get()
は、CSSセレクタを引数として受け取ります。このセレクタに一致する要素をDOMから探し、それを次のコマンドに引き渡します。
cy.get('.my-button') // クラス名 'my-button' を持つ要素を取得
.click(); // 取得した要素をクリック
cy.get()
の主な特徴と利点
-
自動リトライ(Automatic Retries): Cypressのコマンド(特に
cy.get()
)は、要素が見つからない場合や、アサーションが失敗した場合に、デフォルトで一定時間(通常4秒)リトライを試みます。これは、Webアプリケーションが非同期で動作し、要素の表示に時間がかかる場合(例えば、APIからのデータ取得後やアニメーション完了後など)に非常に有用です。開発者が手動でsetTimeout
やwaitFor
のようなものを書く必要がほとんどありません。例:
// 要素がまだ存在しない場合でも、最大4秒間要素の出現を待つ cy.get('#loading-spinner').should('not.exist');
-
チェイニング(Chaining): Cypressのコマンドは、
.
でつなげて(チェインして)記述することができます。cy.get()
で取得した要素は、その後のコマンドに自動的に引き渡されます。cy.get('form') // <form>要素を取得 .find('input[type="text"]') // フォーム内にあるテキスト入力欄を探す .type('テストユーザー') // テキストを入力 .should('have.value', 'テストユーザー'); // 値を検証
-
スコープ(Scoping):
cy.get()
は通常、DOM全体のルート(document
)から要素を探しますが、すでに取得した要素に対して.find()
を使用することで、検索範囲をその要素の子孫に限定できます。これにより、セレクタがシンプルになり、テストの意図が明確になります。cy.get('.product-card') // 製品カード要素を取得 .find('.add-to-cart-button') // その製品カード内のカート追加ボタンを探す .click();
-
可読性と保守性: CSSセレクタは、HTMLの構造を理解していれば誰でも理解しやすく、テストコードの可読性を高めます。また、IDや
data-*
属性(例:data-testid
)などの安定したセレクタを使用することで、UI変更によるテストの破損を防ぎ、保守性を向上させることができます。推奨されるセレクタの使用例:
cy.get('[data-cy="submit-button"]').click(); // data-cy属性を使用 cy.get('#username-input').type('myuser'); // IDを使用
-
デバッグのしやすさ: Cypressのテストランナーは、各
cy.get()
コマンドが実行されたときに、どの要素が取得されたかを可視化してくれます。これにより、テストが失敗した際に、どの要素が期待通りに取得できなかったのかを視覚的に確認でき、デバッグが容易になります。
注意点
-
複数の要素の取得:
cy.get()
はセレクタに一致するすべての要素を取得します。複数の要素が一致し、そのうち特定の要素を操作したい場合は、.eq()
や.first()
、.last()
などのメソッドを使用して、コレクションから特定の要素を選択する必要があります。cy.get('li') // すべての <li> 要素を取得 .eq(0) // 最初の <li> 要素を取得 .click();
-
不安定なセレクタの回避: クラス名が頻繁に変更されたり、DOMの階層が複雑な場合、セレクタが壊れやすくなります。できるだけIDや
data-testid
のようなテスト専用の属性、または意味のあるクラス名など、安定したセレクタを使用することを心がけましょう。
エラー: Timed out retrying: Expected to find element: 'セレクタ', but never found it.
これは最も頻繁に発生するエラーで、「要素が見つからない」ことを意味します。
原因
- iframe内の要素: Cypressはデフォルトではiframe内のDOMにアクセスできません。
- DOMのリロード/再レンダリング: ユーザーアクションやアプリケーションの状態変化により、要素がDOMから一度削除され、新しい要素として再レンダリングされる。Cypressが古い要素を掴んだままになり、アクション実行時に要素が「detached from DOM」となる。
- 要素の遅延ロード: API呼び出しや非同期処理の結果として、要素が表示されるまでに時間がかかる。Cypressはデフォルトでリトライしますが、タイムアウト時間を超えても要素が出現しない場合。
- 要素の非表示/存在しない: 要素がまだDOMに存在しない、または
display: none;
やvisibility: hidden;
などで非表示になっている。 - セレクタの誤り: 指定したCSSセレクタがWebページ上の実際の要素と一致していない。タイプミス、IDとクラス名の混同、要素の構造変更などが考えられます。
トラブルシューティング
- iframe内の要素:
- iframe内の要素を操作する場合は、
cypress-iframe
のようなプラグインを使用するか、手動でiframeのコンテンツを切り替える処理を記述する必要があります。
- iframe内の要素を操作する場合は、
- DOMリロードへの対応:
- 要素がDOMから detached される場合、その要素に対してすぐにアクションを実行しようとするのではなく、再度
cy.get()
で要素を取得し直すことを検討します。特に、アサーションのチェーンではなく、新しいコマンドチェーンを開始する際に注意が必要です。 cy.get('セレクタ').should('exist').and('be.visible').click()
のように、should
アサーションを挟むことで、要素が存在し、可視になるまでリトライさせる効果があります。
- 要素がDOMから detached される場合、その要素に対してすぐにアクションを実行しようとするのではなく、再度
- イベントの待機:
- 要素が表示されるトリガーとなるAPIリクエストやイベントがある場合、
cy.wait()
を使ってそれらの完了を待つことで、要素の確実な出現を保証できます。 cy.intercept()
を使ってAPIリクエストを待機するのが最も確実です。cy.intercept('GET', '/api/users').as('getUsers'); cy.visit('/users'); cy.wait('@getUsers'); // APIリクエストが完了するまで待機 cy.get('.user-list-item').should('have.length.gt', 0);
- 要素が表示されるトリガーとなるAPIリクエストやイベントがある場合、
- タイムアウトの調整:
- 要素のロードに時間がかかる場合は、
cy.get('.slow-loading-element', { timeout: 10000 })
のようにタイムアウト時間を長く設定できます。ただし、安易に長くしすぎるとテスト全体の実行時間が伸びるため、本当に必要な場合のみ検討します。 - グローバルな
defaultCommandTimeout
を変更することも可能ですが、特定のコマンドに絞ってタイムアウトを設定する方が良いプラクティスです。
- 要素のロードに時間がかかる場合は、
- 要素の可視性の確認:
- 要素が存在しないのではなく、単に非表示になっている場合は、
cy.get('.my-element').should('be.visible')
のように可視性をアサートします。 - 強制的に操作したい場合は、
{ force: true }
オプションを使用しますが、これは最終手段と考えるべきです。例えば、cy.get('.hidden-button').click({ force: true })
。
- 要素が存在しないのではなく、単に非表示になっている場合は、
- セレクタの確認:
- 開発者ツール(Developer Tools)を使って、実際にそのセレクタで要素が選択できるか確認します。
- 可能であれば、
id
属性やdata-testid
(またはdata-cy
、data-test
)のような安定したテスト専用の属性をセレクタとして使用します。 - XPathを使用したい場合、CypressはネイティブでXPathをサポートしていませんが、プラグイン(例:
cypress-xpath
)を使用できます。ただし、CSSセレクタの方が推奨されます。
エラー: cy.click() can only be called on a single element. Your subject contained X elements. Pass { multiple: true } if you want to serially click each element.
cy.get()
で複数の要素が取得されたのに、クリックのような単一要素にしか適用できないアクションを実行しようとした場合に発生します。
原因
- 本来、複数の要素に対して同じ操作をしたいが、
{ multiple: true }
オプションを忘れている。 - セレクタが意図せず複数の要素に一致している。
トラブルシューティング
each()
コマンドの使用:- 各要素に対して個別の処理を行いたい場合は、
each()
コマンドを使用します。
cy.get('.item').each(($el, index, $list) => { // 各要素に対して処理を行う cy.wrap($el).click(); // $el を Cypress コマンドでラップする });
- 各要素に対して個別の処理を行いたい場合は、
{ multiple: true }
オプションの使用:- 意図的に複数の要素を順にクリックしたい場合は、
cy.get('.all-buttons').click({ multiple: true })
のように{ multiple: true }
オプションを渡します。これにより、取得されたすべての要素に対してコマンドが順番に実行されます。
- 意図的に複数の要素を順にクリックしたい場合は、
- セレクタの特定化:
- 最も良い方法は、セレクタをより具体的にして、単一の要素にのみ一致するようにすることです。例えば、リストの特定のアイテムをクリックしたい場合は、
cy.get('.item').eq(0).click()
のようにインデックスを指定したり、テキスト内容でフィルタリングするcy.contains()
と組み合わせるなどします。 cy.get('.list-item:nth-child(2)').click()
のようにCSSセレクタ自体をより具体的にすることも可能です。
- 最も良い方法は、セレクタをより具体的にして、単一の要素にのみ一致するようにすることです。例えば、リストの特定のアイテムをクリックしたい場合は、
エラー: CypressError: cy.within() can only be called on a single element. Your subject contained X elements.
cy.within()
コマンドは、単一のDOM要素のスコープ内で後続のCypressコマンドを実行するためのものですが、cy.within()
の前に複数の要素が取得されている場合に発生します。
原因
cy.get()
などで取得した要素が複数あり、それに対してcy.within()
を直接呼び出そうとしている。
トラブルシューティング
each()
とwithin()
の組み合わせ:- 複数の要素のそれぞれに対して
within
スコープで操作したい場合は、each()
コマンドの中でwithin()
を使用します。
cy.get('.product-card').each(($card) => { cy.wrap($card).within(() => { // 各製品カードのスコープ内で操作 cy.get('.add-to-cart-button').click(); cy.get('.product-name').should('be.visible'); }); });
- 複数の要素のそれぞれに対して
- 単一要素への絞り込み:
cy.within()
を呼び出す前に、first()
,last()
,eq(index)
,contains()
などで単一の要素に絞り込みます。
cy.get('.form-group') // 複数のフォームグループがある .first() // 最初のフォームグループに絞る .within(() => { cy.get('input').type('some text'); });
テストが不安定になる(Flaky Tests)
原因
- テスト間の依存関係: あるテストがアプリケーションの状態を変更し、それが次のテストに影響を与える場合。
- 不安定なセレクタ: UIのわずかな変更でセレクタが壊れやすい場合。
- 非同期処理の待機不足: アプリケーションの要素表示や状態変化がCypressのデフォルトのリトライタイムアウト内に間に合わない場合。
- テストの分離とクリーンアップ:
- 各テストが独立して実行できるように、
beforeEach
やafterEach
フックを使用して、テスト前後のアプリケーション状態をリセットします。例えば、ログイン状態のリセットやデータベースのクリーニングなど。 cy.visit()
を各テストの最初に呼び出すことで、毎回クリーンな状態からテストを開始できます。
- 各テストが独立して実行できるように、
- 適切なアサーションの追加:
cy.get().should('be.visible')
やcy.get().should('exist')
など、要素の存在や可視性を明示的にアサートすることで、Cypressの自動リトライが最大限に活用され、安定性が向上します。- 要素内のテキストや属性が正しいかを確認するアサーションを追加することで、テストの信頼性が高まります。
cy.intercept()
によるネットワーク待機:- 非同期で要素が表示される場合、その原因となるAPIリクエストを特定し、
cy.intercept()
でモックするか、完了をcy.wait()
で待機します。これにより、要素が存在することを保証できます。
- 非同期で要素が表示される場合、その原因となるAPIリクエストを特定し、
- 安定したセレクタの使用:
data-testid
やdata-cy
のようなカスタムデータ属性を使用することを強く推奨します。これらはCSSやJavaScriptの変更に影響されにくく、テストの安定性を高めます。- ID属性はユニークであるため、優先的に使用します。
- クラス名やタグ名のみのセレクタは、汎用性が高すぎるため、予期せぬ要素にマッチする可能性があります。可能な限り避けるか、組み合わせて使用します。
一般的なデバッグのヒント
- 開発者ツールのコンソール:
- テスト実行中に、ブラウザの開発者ツールを開き、コンソールで
Cypress.$('セレクタ')
を実行して、Cypressが使用しているjQueryを使って実際に要素が取得できるかを確認できます。 - また、Cypressはコンソールにも多くのデバッグ情報を出力します。
- テスト実行中に、ブラウザの開発者ツールを開き、コンソールで
cy.log()
とdebugger
:- デバッグのために、
cy.log('メッセージ')
を使ってコマンドログに任意のメッセージを出力したり、debugger;
をコードに挿入してブラウザの開発者ツールでブレークポイントを設定し、ステップ実行することができます。
cy.get('.my-element').then(($el) => { cy.log('要素が取得されました:', $el); debugger; // ここで実行が一時停止し、開発者ツールでDOMの状態を確認できる cy.wrap($el).click(); });
- デバッグのために、
- Cypress Test Runnerの活用:
- テストが失敗した際、Cypress Test Runnerの「Command Log」を確認します。
cy.get()
がどのセレクタで要素を見つけようとしたか、その時点でのDOMの状態はどうだったか、などが詳細に表示されます。 - ステップをクリックすると、その時点での画面のスクリーンショットが確認できます。
- テストが失敗した際、Cypress Test Runnerの「Command Log」を確認します。
cy.get()
は、Webページ上のDOM要素(HTML要素)を取得するためのCypressの基本的なコマンドです。CSSセレクタを使用して要素を指定します。
基本的な要素の取得と操作
最も単純な例です。特定のクラス名を持つボタンを取得し、クリックします。
describe('基本的な要素操作', () => {
beforeEach(() => {
// 各テストの前にウェブサイトを訪問
cy.visit('http://localhost:3000/your-app'); // ここをあなたのアプリケーションのURLに置き換えてください
});
it('特定のボタンをクリックする', () => {
// クラス名 'submit-button' を持つ要素を取得し、クリック
cy.get('.submit-button').click();
// クリック後の結果を検証(例:成功メッセージが表示されたか)
cy.get('.success-message').should('be.visible');
});
it('IDを持つ入力欄にテキストを入力する', () => {
// ID 'username-input' を持つ要素を取得し、テキストを入力
cy.get('#username-input').type('myusername');
// 入力されたテキストが正しいか検証
cy.get('#username-input').should('have.value', 'myusername');
});
it('タグ名で要素を取得し、特定のテキストが含まれているか検証する', () => {
// <h1>タグを持つ要素を取得し、そのテキスト内容を検証
cy.get('h1').should('contain', 'ようこそ');
});
});
安定したセレクタの使用 (data-*属性)
クラス名やIDはJavaScriptやCSSの変更で変わりやすい場合があります。テストの安定性を高めるために、テスト専用のdata-*
属性(例: data-cy
, data-testid
)を使用することが推奨されます。
HTMLの例:
<button data-cy="login-button">ログイン</button>
<input type="text" data-testid="email-input">
Cypressコード:
describe('安定したセレクタ', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app');
});
it('data-cy属性を持つボタンをクリックする', () => {
// data-cy="login-button" を持つ要素を取得し、クリック
cy.get('[data-cy="login-button"]').click();
});
it('data-testid属性を持つ入力欄にテキストを入力する', () => {
// data-testid="email-input" を持つ要素を取得し、テキストを入力
cy.get('[data-testid="email-input"]').type('[email protected]');
cy.get('[data-testid="email-input"]').should('have.value', '[email protected]');
});
});
チェイニング (.find(), .first(), .last(), .eq())
cy.get()
で複数の要素が取得される場合や、特定のコンテキスト内で要素を探したい場合に便利です。
describe('チェイニングと要素の絞り込み', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // リストやカードがあるページを想定
});
it('特定のリストアイテム内のボタンをクリックする', () => {
// まず、ID 'user-list' を持つリスト全体を取得
cy.get('#user-list')
// そのリストの中からクラス名 'user-item' を持つ最初の要素を見つける
.find('.user-item')
.first() // 最初のユーザーアイテムに絞り込む
// そのユーザーアイテムの中にあるクラス名 'view-profile-button' を持つボタンを見つけてクリック
.find('.view-profile-button')
.click();
// プロフィールページに遷移したことを検証するなど
cy.url().should('include', '/profile/');
});
it('2番目の製品カードの「カートに追加」ボタンをクリックする', () => {
// すべての製品カードを取得
cy.get('.product-card')
// 2番目の製品カード(インデックスは0から始まるため、eq(1))に絞り込む
.eq(1)
// その製品カード内にある「カートに追加」ボタンを見つけてクリック
.find('.add-to-cart-button')
.click();
});
it('最後の警告メッセージを閉じる', () => {
// すべての警告メッセージを取得
cy.get('.warning-message')
// 最後の警告メッセージに絞り込む
.last()
// そのメッセージ内にある閉じるボタンを見つけてクリック
.find('.close-button')
.click();
// 閉じた後にメッセージが非表示になったことを検証
cy.get('.warning-message').last().should('not.be.visible');
});
});
cy.within() を使ったスコープの限定
特定の親要素の内部でのみ要素を検索したい場合に非常に役立ちます。セレクタが簡潔になり、意図が明確になります。
<form id="login-form">
<input type="text" name="username" placeholder="ユーザー名">
<input type="password" name="password" placeholder="パスワード">
<button type="submit">ログイン</button>
</form>
<form id="signup-form">
<input type="email" name="email" placeholder="メールアドレス">
<input type="password" name="new-password" placeholder="新しいパスワード">
<button type="submit">サインアップ</button>
</form>
describe('cy.within() を使ったスコープ限定', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // フォームがあるページを想定
});
it('ログインフォーム内での操作', () => {
// ID 'login-form' を持つフォームのスコープに入る
cy.get('#login-form').within(() => {
// この中では、セレクタは #login-form の子孫要素のみを探す
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('password123');
cy.get('button[type="submit"]').click();
});
// ログイン後のメッセージを検証
cy.get('.welcome-message').should('contain', 'testuserさん、こんにちは!');
});
it('サインアップフォーム内での操作', () => {
// ID 'signup-form' を持つフォームのスコープに入る
cy.get('#signup-form').within(() => {
cy.get('input[name="email"]').type('[email protected]');
cy.get('input[name="new-password"]').type('strongpassword');
cy.get('button[type="submit"]').click();
});
// サインアップ後のメッセージを検証
cy.get('.signup-success-message').should('be.visible');
});
});
複数の要素の操作 (.each(), { multiple: true })
cy.get()
が複数の要素を返す場合、それらを個別にまたは一括で操作する方法です。
describe('複数の要素の操作', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // 複数のチェックボックスやリストアイテムがあるページを想定
});
it('すべてのチェックボックスをチェックする', () => {
// クラス名 'task-checkbox' を持つすべてのチェックボックスを取得し、複数チェックを許可してクリック
cy.get('.task-checkbox').click({ multiple: true });
// すべてのチェックボックスがチェックされていることを検証
cy.get('.task-checkbox').should('be.checked');
});
it('各製品の情報を取得し、表示されているか検証する', () => {
// クラス名 'product-name' を持つすべての製品名要素を取得
cy.get('.product-name').each(($el, index, $list) => {
// 各要素に対して個別の操作や検証を行う
cy.log(`製品 ${index + 1}: ${$el.text()}`); // コンソールに製品名を出力
cy.wrap($el).should('be.visible'); // 各製品名が可視であることを検証
});
// 特定の製品数が表示されていることを検証
cy.get('.product-name').should('have.length', 5);
});
});
アサーションとの組み合わせ
cy.get()
の後にshould()
アサーションを続けることで、要素の状態を検証し、Cypressの自動リトライ機能が最大限に活かされます。
describe('アサーションとの組み合わせ', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app');
});
it('ロード後に要素が表示されるのを待つ', () => {
// ID 'loading-spinner' が存在しないことを確認することで、ロード完了を待つ
cy.get('#loading-spinner').should('not.exist');
// その後、コンテンツが表示されたことを確認
cy.get('.main-content').should('be.visible');
});
it('入力フィールドがdisabledでないことを確認し、入力する', () => {
// ID 'input-field' がdisabledでないことを確認
cy.get('#input-field').should('not.be.disabled');
// テキストを入力
cy.get('#input-field').type('Enabled!');
});
it('要素が特定のクラスを持っていることを確認する', () => {
// ID 'status-message' が 'success' クラスを持っていることを確認
cy.get('#status-message').should('have.class', 'success');
});
});
cy.contains()
cy.contains()
は、要素のテキスト内容に基づいて要素を取得したい場合に非常に強力です。特に、セレクタが不安定な場合や、UI上に表示されているテキストを基準に要素を特定したい場合に役立ちます。
主な用途
- リスト内の特定のテキストを含むアイテムを見つける。
- ボタンやリンクのテキスト内容で要素を特定する。
基本的な使い方
cy.contains(text)
: ページ全体から指定されたテキストを含む要素を探します。
cy.contains(selector, text)
: 指定されたセレクタに一致する要素の中から、指定されたテキストを含む要素を探します。
例
describe('cy.contains() の使用例', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // ログインボタンやリストがあるページを想定
});
it('「ログイン」と書かれたボタンをクリックする', () => {
// ページ上の「ログイン」というテキストを含む要素(通常はボタンやリンク)をクリック
cy.contains('ログイン').click();
cy.url().should('include', '/dashboard'); // ログイン後にダッシュボードに遷移したか確認
});
it('製品リストから特定の名前の製品を見つける', () => {
// クラス名 'product-item' を持つ要素の中から「Cypress Tシャツ」というテキストを含む要素を見つける
cy.contains('.product-item', 'Cypress Tシャツ')
.find('.add-to-cart-button') // その製品アイテム内の「カートに追加」ボタンを見つける
.click();
cy.get('.cart-count').should('contain', '1'); // カートの数が増えたか確認
});
});
cy.get()との比較
cy.contains()
は、特に開発者がdata-testid
などのテスト用属性を付けていない場合に有用ですが、テキスト内容が変わるとテストが壊れる可能性があります。cy.get()
はセレクタに厳密に一致する要素を取得するのに対し、cy.contains()
は可視のテキスト内容を基準に要素を探します。
cy.find() (既存の要素の内部を検索)
cy.find()
は、すでに取得したDOM要素の内部から、子孫要素を検索したい場合に使用します。cy.get()
が常にdocument
ルートから検索するのに対し、cy.find()
は検索範囲を限定します。これにより、セレクタがシンプルになり、テストの意図が明確になります。
主な用途
- 複数の類似したコンポーネントがある場合、親要素を特定してからその内部の要素を操作する。
- 親要素のスコープ内で特定の子要素を操作する。
基本的な使い方
cy.get('parent-selector').find('child-selector')
例
describe('cy.find() の使用例', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // 製品カードやフォームがあるページを想定
});
it('特定の製品カード内の詳細ボタンをクリックする', () => {
// まず、ID 'product-card-123' を持つ製品カードを取得
cy.get('#product-card-123')
// その製品カードの中からクラス名 'detail-button' を持つボタンを探してクリック
.find('.detail-button')
.click();
cy.url().should('include', '/products/123'); // 製品詳細ページに遷移したか確認
});
it('フォーム内のエラーメッセージのテキストを検証する', () => {
// ID 'login-form' を持つフォームを送信(エラーを発生させる)
cy.get('#login-form').submit(); // ここではフォーム送信でエラーが発生すると仮定
// フォームの中からクラス名 'error-message' を持つ要素を探し、テキストを検証
cy.get('#login-form')
.find('.error-message')
.should('contain', 'ユーザー名またはパスワードが不正です');
});
});
cy.get()との比較
cy.find()
はcy.get().within()
と似ていますが、cy.find()
は直接要素を取得し、チェーンを継続するのに対し、cy.within()
は新しいスコープを作成し、その中でコマンドを実行します。cy.get()
はページのどこからでも要素を取得できますが、cy.find()
は前のコマンドで取得された要素の子孫要素のみを検索します。
cy.get().filter(), cy.get().not(), cy.get().first(), cy.get().last(), cy.get().eq() (コレクションからの絞り込み)
cy.get()
が複数の要素(要素のコレクション)を返す場合、これらのコマンドを使って、そのコレクションから特定の要素をフィルタリングして取得できます。
主な用途
- 特定の要素を除外する。
- 最初の要素、最後の要素、または特定のインデックスの要素を取得する。
- リストアイテムの中から特定の条件を満たすものだけを選択する。
例
describe('コレクションからの絞り込み', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // 複数のアイテムがあるリストを想定
});
it('アクティブなクラスを持つリストアイテムだけをクリックする', () => {
// すべてのリストアイテムを取得し、その中からクラス名 'active' を持つものだけをフィルタリング
cy.get('.list-item')
.filter('.active')
.click(); // アクティブなアイテムをクリック
});
it('最初のリストアイテムのテキストを検証する', () => {
// すべてのリストアイテムを取得し、最初のものに絞り込む
cy.get('.list-item')
.first()
.should('contain', '最初のアイテム');
});
it('3番目のリストアイテムのチェックボックスをチェックする', () => {
// すべてのリストアイテムを取得し、3番目のもの(インデックス2)に絞り込む
cy.get('.list-item')
.eq(2)
.find('input[type="checkbox"]') // そのアイテム内のチェックボックスを探す
.check();
});
it('完了したタスクを除外して、残りのタスク数を検証する', () => {
// すべてのタスクアイテムを取得し、クラス名 'completed' を持たないものだけを残す
cy.get('.task-item')
.not('.completed')
.should('have.length', 3); // 残りのタスクが3つであることを検証
});
});
cy.get()との比較
- これらのメソッドは、
cy.get()
が複数の要素を返した後に、その結果セットをさらに絞り込むために使われます。cy.get()
だけでは不可能な、より複雑な要素の選択を可能にします。
cy.get().parent(), cy.get().parents(), cy.get().siblings() (DOMツリーを遡る/横断する)
要素の親子関係や兄弟関係を利用して要素を取得したい場合に便利です。
主な用途
- 特定の要素の兄弟要素にアクセスする。
- 特定の要素の親要素や祖先要素を操作する。
例
describe('DOMツリーの操作', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/your-app'); // ボタンの近くにテキストがあるようなページを想定
});
it('クリックしたボタンの隣のステータステキストを検証する', () => {
// ID 'my-button' を持つボタンをクリック
cy.get('#my-button').click();
// そのボタンの直前の兄弟要素(例えば、ステータステキスト)を取得し、検証
cy.get('#my-button')
.prev('.status-text') // 直前の兄弟要素で、クラス名 'status-text' を持つもの
.should('contain', '処理が完了しました');
});
it('ある入力フィールドの親要素のスタイルを検証する', () => {
// ID 'email-input' を持つ入力フィールドを取得
cy.get('#email-input')
.parent() // その親要素(通常は<div>や<label>など)を取得
.should('have.class', 'input-group');
});
});
cy.get()との比較
cy.get()
は基本的にCSSセレクタに基づいて子孫要素を検索しますが、これらのコマンドはDOMツリーを遡ったり(parent, parents)、**横断したり(siblings, next, prev)**することができます。
cy.get()
はCypressの要素取得の基礎ですが、これらの代替コマンドを適切に使い分けることで、より意図が明確で、柔軟かつ堅牢なテストコードを記述することができます。
- 要素の親子・兄弟関係を利用して取得したい:
cy.parent()
,cy.parents()
,cy.siblings()
,cy.next()
,cy.prev()
- 複数要素のコレクションから特定の要素を絞り込みたい:
cy.filter()
,cy.not()
,cy.first()
,cy.last()
,cy.eq()
- 特定の親要素の内部で検索したい:
cy.find()
(またはcy.within()
) - テキストで取得したい:
cy.contains()