JavaScriptテスト自動化:Cypress select()で効率的なドロップダウン操作
Cypressの select()
コマンドは、HTMLの <select>
要素(ドロップダウンリスト)のオプションを選択するために使用されます。ユーザーがウェブサイト上でドロップダウンメニューから項目を選ぶ操作を、自動テストの中でシミュレートするのに役立ちます。
具体的には、select()
コマンドは、選択したいオプションの 値 (value)、表示テキスト (text)、または インデックス (index) を指定することで、目的のオプションを選択します。
以下に、それぞれの指定方法と例を挙げます。
値 (value) で選択する場合
`<select id="fruits">
<option value="apple">Apple</option> <option value="banana">Banana</option> <option value="cherry">Cherry</option> </select>`
この <select>
要素で "Banana" を選択したい場合、以下のように記述します。
cy.get('#fruits').select('banana');
select()
には、<option>
タグの value
属性の値を文字列として渡します。
表示テキスト (text) で選択する場合
上記の <select>
要素で "Cherry" を選択したい場合、以下のように記述します。
cy.get('#fruits').select('Cherry');
select()
には、<option>
タグに表示されているテキストを文字列として渡します。大文字・小文字は区別されますので注意が必要です。
インデックス (index) で選択する場合
上記の <select>
要素で最初のオプション ("Apple") を選択したい場合、以下のように記述します。インデックスは 0 から始まります。
cy.get('#fruits').select(0);
2番目のオプション ("Banana") を選択する場合は cy.get('#fruits').select(1);
となります。
複数のオプションを選択する場合 (multiple 属性を持つ <select> 要素)
`<select id="colors" multiple>
<option value="red">Red</option> <option value="green">Green</option> <option value="blue"</1>>Blue</option> </select>`
multiple
属性を持つ <select>
要素では、複数のオプションを同時に選択できます。select()
コマンドに、選択したい値、テキスト、またはインデックスの配列を渡すことで実現できます。
// 値で複数選択
cy.get('#colors').select(['red', 'blue']);
// テキストで複数選択
cy.get('#colors').select(['Green', 'Blue']);
// インデックスで複数選択
cy.get('#colors').select([1, 2]);
- 選択が成功したかどうかをアサーションで検証することも一般的です。例えば、選択された値やテキストが期待通りであることを確認できます。
select()
コマンドを実行すると、その<select>
要素の状態が変更され、選択されたオプションがアクティブになります。
<!-- end list -->
cy.get('#fruits').select('banana').should('have.value', 'banana');
cy.get('#fruits').select('Cherry').should('contain', 'Cherry'); // 表示テキストで検証する場合
対象の要素が見つからない (cy.get() エラー)
- トラブルシューティング
- セレクタが正しいか、開発者ツールで確認する。
- 要素が表示されるまで待機する (
cy.wait()
,cy.intercept()
などを使用して、関連するAPIリクエストの完了を待つ、または適切なアサーションで要素の表示を待つ)。 - 親要素のスコープを絞り込んでいる場合は、それが正しいか確認する。
- 原因
- セレクタ (
#nonExistentId
など) が間違っている。 - 要素が非表示になっている (CSSの
display: none
,visibility: hidden
など)。 - 要素がDOMにまだロードされていない (非同期処理による遅延など)。
- セレクタ (
- エラー内容
CypressError: Timed out retrying after 4000ms: cy.get('#nonExistentId') failed because this element was never visible.
のように、指定したセレクタに一致する要素がDOM内に存在しない、または選択可能になる前にタイムアウトする。
選択したいオプションが存在しない (cy.select() エラー)
- トラブルシューティング
<select>
要素の<option>
要素の内容を開発者ツールで確認し、select()
に渡す値が正しいか確認する。<option>
要素が動的に生成される場合は、それらがDOMにロードされるまで待機する。関連するAPIリクエストの完了を待つ、または適切なアサーションでオプションの存在を確認する。
- 原因
select()
に渡した値、テキスト、またはインデックスが間違っている。<option>
要素が動的に変更されている (テスト実行時にオプションがまだロードされていないなど)。
- エラー内容
CypressError: cy.get('#fruits').select('grape') failed because the given value 'grape' is not a valid <option>. Available options are: apple, banana, cherry
のように、select()
に渡した値、テキスト、またはインデックスに対応する<option>
要素が存在しない。
複数選択 (multiple) が許可されていない <select> 要素で配列を渡した場合
- トラブルシューティング
- 対象の
<select>
要素にmultiple
属性があるか確認する。 - 単一選択の
<select>
要素に対しては、文字列または数値 (インデックス) を一つだけselect()
に渡すようにする。
- 対象の
- 原因
- 対象の
<select>
要素が複数選択を許可していないのに、配列形式で値を渡している。
- 対象の
- エラー内容
CypressError: cy.get('#singleSelect').select(['value1', 'value2']) failed because this <select> does not have the 'multiple' attribute.
のように、multiple
属性がない<select>
要素に対して、複数の値を配列でselect()
に渡そうとした場合。
select() が意図したオプションを選択しない
- トラブルシューティング
- より明確なセレクタを使用する (例えば、特定の属性値を持つオプションを選択するなど)。
select()
に渡す値やテキストが完全に一致しているか確認する (余分な空白なども含めて)。- 必要であれば、正規表現を使用して部分一致で選択することも検討する (ただし、可読性は低下する可能性がある)。
- 原因
- 値、テキスト、またはインデックスが類似した別のオプションと一致してしまっている。
<option>
要素の値やテキストに予期せぬ空白が含まれている。
- エラー内容
エラーは発生しないが、テスト後に確認すると、期待したオプションが選択されていない。
イベントの発火に関する問題
- トラブルシューティング
select()
の後に、必要に応じて.trigger('change')
を明示的に呼び出してイベントを発火させることを検討する。- イベントリスナーが正しく動作しているか、ウェブアプリケーションのコードを確認する。
- イベントが発火するのを待つために、適切な
cy.wait()
やアサーションを使用する。
- 原因
select()
コマンド自体は要素の状態を変更するだけで、ブラウザのネイティブなイベント発火を完全にシミュレートしない場合がある。- イベントリスナーが正しく設定されていない、またはテストのタイミングが合っていない。
- エラー内容
オプションは選択されるが、関連するイベント (例えばchange
イベント) が期待通りに発火しない、またはテストがそのイベントを捕捉できない。
- 公式ドキュメントを参照する
Cypress の公式ドキュメントは、各コマンドの詳細な説明や使用例、ベストプラクティスが豊富に記載されています。 - テストを小さく保つ
問題が発生した場合に、原因を特定しやすくするために、テストをできるだけ小さく分割することを心がけます。 - 開発者ツールを併用する
ブラウザの開発者ツール (特に Elements タブと Console タブ) を使用して、DOM構造、要素の属性、エラーメッセージなどを確認します。 - cy.log() を使用する
途中の要素の状態や変数の値などをログ出力することで、問題の切り分けに役立ちます。 - Cypress Runner を活用する
Cypress Runner は、テストの実行状況をリアルタイムで確認でき、エラーメッセージやDOMの状態を視覚的に把握するのに非常に役立ちます。
例1: value 属性で選択する
HTML: `<select id="product">
<option value="apple">リンゴ</option> <option value="banana">バナナ</option> <option value="orange">オレンジ</option> </select>`
Cypress テストコード:
describe('セレクトボックスのテスト', () => {
it('value属性でオプションを選択する', () => {
cy.visit('/path/to/your/page.html'); // テスト対象のページにアクセス
// id が "product" のセレクトボックスで、value が "banana" のオプションを選択
cy.get('#product').select('banana');
// 選択された値が期待通りであることをアサーションで確認
cy.get('#product').should('have.value', 'banana');
});
});
この例では、cy.get('#product')
で <select>
要素を取得し、.select('banana')
で value
属性が "banana" の <option>
を選択しています。その後、.should('have.value', 'banana')
で、実際に選択された <select>
要素の値が "banana" であることを検証しています。
例2: 表示テキストで選択する
HTML (例1と同じ): `<select id="product">
describe('セレクトボックスのテスト', () => {
it('表示テキストでオプションを選択する', () => {
cy.visit('/path/to/your/page.html');
// id が "product" のセレクトボックスで、表示テキストが "オレンジ" のオプションを選択
cy.get('#product').select('オレンジ');
// 選択された値が期待通りであることをアサーションで確認
cy.get('#product').should('have.value', 'orange');
});
});
ここでは、.select('オレンジ')
のように、<option>
タグに表示されているテキスト "オレンジ" を指定してオプションを選択しています。アサーションでは、選択された結果の value
が "orange" であることを確認しています。
例3: インデックスで選択する
describe('セレクトボックスのテスト', () => {
it('インデックスでオプションを選択する', () => {
cy.visit('/path/to/your/page.html');
// id が "product" のセレクトボックスで、インデックスが 1 (2番目のオプション) のものを選択
cy.get('#product').select(1);
// 選択された値が期待通りであることをアサーションで確認
cy.get('#product').should('have.value', 'banana');
});
});
.select(1)
のように、数値 (インデックス) を渡すことで、指定されたインデックスのオプションを選択できます。インデックスは 0 から始まるため、1 は2番目のオプション ("バナナ") を指します。
例4: multiple
属性を持つセレクトボックスで複数のオプションを選択する
HTML: `<select id="colors" multiple>
<option value="red">赤</option> <option value="green">緑</option> <option value="blue">青</option> </select>`
describe('複数選択セレクトボックスのテスト', () => {
it('複数のオプションをvalue属性で選択する', () => {
cy.visit('/path/to/your/page.html');
// id が "colors" の複数選択可能なセレクトボックスで、value が "red" と "blue" のオプションを選択
cy.get('#colors').select(['red', 'blue']);
// 選択された値が期待通りであることをアサーションで確認 (配列で比較)
cy.get('#colors').should('have.value', ['red', 'blue']);
});
it('複数のオプションを表示テキストで選択する', () => {
cy.visit('/path/to/your/page.html');
cy.get('#colors').select(['緑', '青']);
cy.get('#colors').should('have.value', ['green', 'blue']);
});
it('複数のオプションをインデックスで選択する', () => {
cy.visit('/path/to/your/page.html');
cy.get('#colors').select([0, 2]);
cy.get('#colors').should('have.value', ['red', 'blue']);
});
});
multiple
属性を持つ <select>
要素では、.select()
に配列を渡すことで複数のオプションを同時に選択できます。値、表示テキスト、インデックスのいずれの方法でも配列で指定できます。アサーションで選択された値を確認する際も、配列で期待される値を指定します。
- より複雑なシナリオでは、
<select>
要素が動的に更新されたり、非同期処理の結果でオプションが追加されたりする場合があります。そのような場合は、適切なcy.wait()
やアサーションを使用して、要素が期待される状態になるまで待機する必要があります。 .should('have.value', ...)
は、選択された<option>
のvalue
属性を検証する一般的な方法です。表示テキストで選択した場合でも、通常はvalue
属性で検証を行います。- これらの例では、テスト対象の HTML ファイルが
/path/to/your/page.html
に存在することを前提としています。実際のパスに合わせてcy.visit()
の引数を変更してください。
.click() を使用してオプションを直接クリックする
<select>
要素がカスタムスタイルで実装されていたり、通常の <select>
要素とは異なるUI/UXを提供している場合、.select()
コマンドが期待通りに動作しないことがあります。このような場合、展開されたドロップダウンリスト内のオプションを直接 .click()
でクリックする方法が有効です。
HTML (例: カスタムスタイルされたドロップダウン): `<div class="custom-select">
<div class="selected-option">選択してください</div> <ul class="options"> <li data-value="apple">リンゴ</li> <li data-value="banana">バナナ</li> <li data-value="orange">オレンジ</li>
</ul> &lt;/div&gt;`
describe('カスタムセレクトボックスのテスト', () => {
it('クリックでオプションを選択する', () => {
cy.visit('/path/to/your/custom_select.html');
// ドロップダウンを開く要素をクリック
cy.get('.custom-select .selected-option').click();
// 選択したいオプションをクリック (data-value 属性で絞り込む例)
cy.get('.custom-select .options li[data-value="banana"]').click();
// 選択された結果を検証 (例: 表示テキストの変化)
cy.get('.custom-select .selected-option').should('contain', 'バナナ');
});
});
この方法では、まずドロップダウンを開く要素をクリックし、その後、目的のオプション要素をセレクタで特定して .click()
します。カスタム属性 (data-value
) などを使用して、より正確にオプションを特定することが重要です。
キーボード操作を使用する
<select>
要素は、キーボード操作 (矢印キーでの移動、Enterキーでの選択など) で操作することも可能です。Cypressでは、.type()
コマンドと特殊キーの組み合わせで、これらの操作をシミュレートできます。
HTML (通常の <select>
要素):
`<select id="fruits">
describe('キーボード操作でセレクトボックスを操作するテスト', () => {
it('矢印キーとEnterキーでオプションを選択する', () => {
cy.visit('/path/to/your/page.html');
// セレクトボックスにフォーカスを当てる
cy.get('#fruits').focus();
// 下矢印キーを2回押して "さくらんぼ" に移動
cy.get('#fruits').type('{downarrow}').type('{downarrow}');
// Enterキーを押して選択を確定
cy.get('#fruits').type('{enter}');
// 選択された値が期待通りであることをアサーションで確認
cy.get('#fruits').should('have.value', 'cherry');
});
});
この例では、.focus()
で <select>
要素にフォーカスを当て、.type('{downarrow}')
で下矢印キーを送信してオプションを移動し、.type('{enter}')
で選択を確定しています。
ライブラリやユーティリティ関数の利用
複雑なUIを持つセレクトボックスの場合、独自のユーティリティ関数や、Cypressのコミュニティが提供するライブラリを利用することも考えられます。これらのライブラリは、特定のUIパターンに対応した便利なコマンドや関数を提供している場合があります。
例えば、非同期でロードされるオプションを持つセレクトボックスの操作を支援するカスタムコマンドを作成することも可能です。
// cypress/support/commands.js
Cypress.Commands.add('selectAsyncOption', (selector, value) => {
cy.get(selector).click();
cy.get('.async-options').contains(value).click(); // 例: 非同期ロードされたオプションを持つ要素
});
テストコード:
describe('非同期ロードされるセレクトボックスのテスト', () => {
it('カスタムコマンドでオプションを選択する', () => {
cy.visit('/path/to/your/async_select.html');
cy.selectAsyncOption('#asyncSelect', 'Option B');
cy.get('#asyncSelect').should('have.value', 'option_b');
});
});
このように、.select()
コマンドが直接適用できない場合でも、UIの構造や動作に合わせて、.click()
, .type()
, カスタムコマンド、または外部ライブラリを組み合わせることで、<select>
要素の操作を自動化できます。