はじめに
この記事では、一連のCypressコマンドのリトライを行なってCanvas要素のテストの安定性を向上させる方法を紹介します。
Canvas要素のCypressテストにおける課題
Canvas要素は、グラフィックやアニメーションなどを動的に描画するために使用されます。これらの要素はDOM内の個別要素として表現されないため、通常のセレクタでの検証が困難です。また、Canvasの内容はJavaScriptによって動的に生成されるため、その非同期性からもその状態をテストで把握するのは一筋縄ではいきません。
リトライの実装
私が直面した問題は、Canvas内の特定のクリック可能な領域がレンダリングされる前にCypressのdblclickコマンドが実行され、クリックが無効のまま結果的にその後、予期した要素が見つからないというものでした。
Cypress.Commands.add("openCanvasNode", (position) => {
// Canvas内の特定のポジションを指定してダブルクリック
cy.get("[data-cy=canvas]").dblclick(position.x, position.y);
// Note: クリック後、例えば画面遷移してパンクズリストが現れることを想定
// しかし、クリック領域がまだ描画されていなかったら
// ダブルクリックが無効になり意図した画面に遷移しない状況
cy.get("[data-cy=breadcrumb]"); // 遷移されなかった場合はここでタイムアウトエラーになる
});
色々アプローチを考えた結果(後述します)、選んだ解決策として次のようなリトライロジックを組み込むことにしました。
Cypress.Commands.add("openCanvasNode", (position, retries = 3) => {
const MAX_RETRIES = retries;
let currentRetry = 0;
const checkBreadcrumbExists = () => cy.get('body').find("[data-cy=breadcrumb]").length > 0;
const tryOpeningNode = () => {
cy.get("[data-cy=canvas]").dblclick(position.x, position.y);
if (checkBreadcrumbExists()) {
cy.get("[data-cy=breadcrumb]");
cy.log("Node opened successfully");
} else if (currentRetry < MAX_RETRIES) {
currentRetry++;
cy.log(`Node not opened. Retrying attempt ${currentRetry}/${MAX_RETRIES}`);
cy.wait(1000); // Wait for 1 second before retrying
tryOpeningNode(); // Recursively try to open the node
} else {
cy.log("Maximum retries reached. Node did not open.");
// throw error
}
});
tryOpeningNode();
});
コマンド利用するテストのシンプルな例
describe('Canvas Node Test', () => {
it('should open a node on the canvas', () => {
cy.visit('/your-canvas-page');
const clickPosition = { x: 100, y: 200 }; // Example click position
cy.openCanvasNode(clickPosition);
});
});
Cypressは個々のコマンドを自動的にリトライしますが、一連のコマンドを丸ごとリトライする機能はサポートしていません。したがって上記のようなリトライ実装を行うことで、初期の信頼性の低いアクション(canvasのdblclick)が後続のアクションに影響を与えるケースを打破しました。
他の試みたアプローチ
- ダイナミックにdata-cy attributeを設定。理論上は可能なはずですが、特定の位置が完全に描画されるタイミングを特定して data-cy を要素にセットするのには困難がありましたので諦めました
- Cypressの
fail
イベントを使用してリトライ: この方法でも機能しますが、すべての予期しないエラーも食ってしまうのであくまでデバッグ用として用意されているイベントであるため利用することは公式にて推奨されていません Cypress-Events参照 - dblclick 前に cy.wait(X)を使用: この方法も機能しますが、一般的に推奨されていません
- Canvas内の特定のポジションが意図したバックグラウンドカラーに変更しているかチェック: 第二候補でしたがより複雑になりそうだったので却下
まとめ
この記事では CypressでCanvas要素のテストを行う際、その動的な性質を考慮に入れ、リトライロジックを組み込むことでテストを安定化させる方法を紹介しました