Cypress Commandsの"next":非同期処理とキューシステムを理解して使いこなす
非同期処理とキューシステム
Cypress コマンドは非同期で実行されます。つまり、コマンドが実行されるとすぐに次の行に進まず、コマンドが完了するまで待機します。この非同期処理を管理するために、Cypress は内部的にキューシステムを使用します。キューシステムは、実行待ちのコマンドを順番に保持し、処理の準備が整った順に実行します。
コマンドチェーンと next
複数のコマンドを連続して実行したい場合は、コマンドチェーンと呼ばれる構文を使用します。コマンドチェーンは、.
演算子で連結された一連のコマンドで構成されます。例えば、以下のコードは、要素を取得し、その要素に値を入力し、最後にその要素のアサーションを行うコマンドチェーンを表します。
cy.get('input[type="text"]').type('Hello, world!').should('have.value', 'Hello, world!');
このコマンドチェーンでは、cy.get()
コマンドは要素を取得し、その結果を次の cy.type()
コマンドに渡します。同様に、cy.type()
コマンドの結果は cy.should()
コマンドに渡されます。
ここで next
関数の役割が登場します。next
関数は、コマンドチェーン内の次のコマンドをキューに追加します。しかし、その前に、現在実行中のコマンドを完了させます。つまり、next
関数は、コマンドチェーン内の次のコマンドに進む前に、前のコマンドが完了するのを確実にします。
next の具体的な使用方法
next
関数は、主に以下の 2 つのシナリオで使用されます。
- 非同期処理の完了を待つ場合
非同期処理が完了するのを待たずに次のコマンドに進みたい場合は、next
関数を使用します。例えば、以下のコードは、非同期 API 呼び出しを行い、その結果を次のコマンドに渡す例です。
cy.request('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
const postData = response.body;
// ...
next(postData);
});
この例では、cy.request()
コマンドは非同期的に API を呼び出し、その結果を response
変数に格納します。その後、then()
コールバック内で postData
変数を next
関数に渡します。これにより、next
関数は、API 呼び出しが完了してから次のコマンドに postData
を渡します。
- テストの進行を制御する場合
テストの進行を制御するために next
関数を使用することもできます。例えば、以下のコードは、条件に応じてテストのフローを分岐する例です。
cy.get('button').click()
.then(() => {
if (someCondition) {
next();
} else {
// ...別の処理を実行
}
});
この例では、cy.get('button').click()
コマンドはボタンをクリックし、その結果を then()
コールバックに渡します。その後、then()
コールバック内で someCondition
変数をチェックします。someCondition
が真の場合、next
関数を呼び出してテストを続行します。そうでない場合は、// ...別の処理を実行
で示されるように、別の処理を実行します。
next の注意点
next
関数は、Cypress バージョン 4.0 以降でのみ使用できます。next
関数は、テストの進行を制御するために使用できますが、乱用するとテストコードが複雑になり、理解しにくくなる可能性があります。next
関数は、非同期処理が完了するのを確実に待機してから次のコマンドに進みます。そのため、テストの実行速度に影響を与える可能性があります。
代替手段
next
関数の代わりに、以下の代替手段を使用することもできます。
- Promise を使用する
非同期処理を扱う場合は、Promise を使用してコードをより明確に記述することができます。
- コールバック関数を使用する
テストの進行を制御する場合は、コールバック関数を使用してコードを記述することができます。
非同期処理の完了を待つ
cy.request('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
const postData = response.body;
next(postData);
})
.then((postData) => {
// ...postData を使用して処理を行う
});
この例では、cy.request()
コマンドは非同期的に API を呼び出し、その結果を postData
変数に格納します。その後、next
関数を呼び出して postData
を次のコマンドに渡します。次のコマンドは、postData
を使用して処理を行います。
テストの進行を制御する
cy.get('button').click()
.then(() => {
if (someCondition) {
next();
} else {
// ...別の処理を実行
}
})
.then(() => {
// ...次の処理を行う
});
この例では、cy.get('button').click()
コマンドはボタンをクリックし、その結果を then()
コールバックに渡します。その後、then()
コールバック内で someCondition
変数をチェックします。someCondition
が真の場合、next
関数を呼び出してテストを続行します。そうでない場合は、// ...別の処理を実行
で示されるように、別の処理を実行します。その後、次の then()
コールバック内で次の処理を行います。
コマンドチェーンと next を組み合わせる
cy.get('input[type="text"]').type('Hello, world!')
.next()
.should('have.value', 'Hello, world!');
この例では、cy.get('input[type="text"]').type('Hello, world!')
コマンドは要素を取得し、その要素に値を入力します。その後、next
関数を呼び出して次のコマンドに進む前に、現在のコマンドが完了するのを確実にします。最後に、should('have.value', 'Hello, world!')
コマンドは、要素のアサーションを行います。
カスタムコマンドと next を使用する
Cypress.Commands.add('login', (username, password) => {
cy.get('input[name="username"]').type(username);
cy.get('input[name="password"]').type(password);
cy.get('button[type="submit"]').click();
next();
});
cy.login('user123', 'password123')
.then(() => {
// ...ログイン後の処理を行う
});
この例では、Cypress.Commands.add()
関数を使用して login
というカスタムコマンドを作成します。このコマンドは、ユーザー名とパスワードを受け取り、ログイン処理を実行します。その後、next
関数を呼び出して次のコマンドに進む前に、現在の処理が完了するのを確実にします。
Promise を使用する
非同期処理を扱う場合は、Promise を使用してコードをより明確に記述することができます。Promise は、非同期処理の結果を扱うための標準的な JavaScript API です。
cy.request('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
const postData = response.body;
return postData;
})
.then((postData) => {
// ...postData を使用して処理を行う
});
この例では、cy.request()
コマンドは非同期的に API を呼び出し、その結果を Promise として返します。その後、then()
メソッドを使用して Promise の結果を処理します。最初の then()
メソッドは、postData
変数に API レスポンスの本文を格納します。2 番目の then()
メソッドは、postData
を使用して処理を行います。
コールバック関数を使用する
テストの進行を制御する場合は、コールバック関数を使用してコードを記述することができます。コールバック関数は、非同期処理が完了した後に呼び出される関数です。
cy.get('button').click((done) => {
if (someCondition) {
done();
} else {
// ...別の処理を実行
}
});
// ...次の処理を行う
この例では、cy.get('button').click()
コマンドはボタンをクリックし、コールバック関数 done
を渡します。その後、then()
コールバック内で someCondition
変数をチェックします。someCondition
が真の場合、done()
関数を呼び出してテストを続行します。そうでない場合は、// ...別の処理を実行
で示されるように、別の処理を実行します。
非同期/待機コマンドを使用する
Cypress には、非同期処理を待機するための組み込みコマンドが用意されています。これらのコマンドを使用することで、next
関数を使用せずにコードを記述することができます。
cy.route()
: スタブ API エンドポイントを作成します。cy.fixture()
: ローカルファイルからデータを非同期的に読み込みます。cy.wait()
: 特定の条件が満たされるまで待機します。
方法 | 利点 | 欠点 |
---|---|---|
next | 簡潔で使いやすい | 乱用するとコードが複雑になる可能性がある |
Promise | コードが明確で読みやすい | 多少冗長になる可能性がある |
コールバック関数 | テストの進行を詳細に制御できる | コードが煩雑になる可能性がある |
非同期/待機コマンド | コードが簡潔で読みやすい | すべての状況で使用できるわけではない |