はじめに
こんにちは。Xイノベーション本部の稲丸です。
みなさんは、E2Eテストをどのように作成・実施されていますか?
人力で実施している、Seleniumで自動化している、などさまざまな方法が考えられますが、最近はCypressを利用されている方も多いのではないでしょうか。
ISIDでは、Ci*X Workflowという汎用ワークフローシステムの開発において、Cypressを用いてE2Eテストを作成・自動化しています。
今回は、E2EテストフレームワークCypressをご紹介します。
Cypress
Cypressは、JavaScriptで実装されたオープンソースのE2Eテストフレームワークです。
多くのE2Eテストフレームワークと異なり、Seleniumに依存していない点が特徴の1つです。
また、テストの実装に必要なライブラリの多くをバンドルしており、基本的なテストであればCypress以外のライブラリをインストールすることなく実装できます。
導入、操作手順
Cypressはnpmパッケージとして提供されているため、Node.jsがインストールされた環境下で下記のコマンドを実行するとインストールできます。
npm install cypress --save-dev
Cypressには、GUIツールとCLIツールがそれぞれ用意されています。
下記のコマンドで、GUIツール(管理画面)を立ち上げることができます。
npx cypress open
初回起動時など、プロジェクト内にテストコードが存在しない場合は、管理画面上からテンプレートプロジェクトを作成できます。
プロジェクト内にテストコードが存在する場合は、管理画面から作成したテストを実行できます。
また、CLIツールを用いて、作成したテストをコマンドで実行することもできます。
(下記の例では、実行ブラウザとしてChromeを指定しています)
npx cypress run --browser chrome
テストの設定は、cypress.config.js(.ts)
という設定ファイルに記述します。
import { defineConfig } from "cypress"; export default defineConfig({ e2e: { baseUrl: "http://localhost:3000", screenshotOnRunFailure: false, video: false, specPattern: [ "cypress/e2e/spec.cy.ts", "cypress/e2e/another-spec.cy.ts" ], }, });
通常、specPatternは実行するテストファイル名のパターンを記述します(デフォルト値はcypress/e2e/**/*.cy.{js,jsx,ts,tsx}
)。一方、上記の例のように配列形式でテストファイル名を記述することで、テストの実行順を制御できます。
他にも多くのパラメータが設定可能なので、詳細はこちらをご参照ください。
テストコードの実装方法
まず、cypress/e2e/
配下にテストファイルを作成します。管理画面上から、「New Spec」→「Create new empty spec」とクリックして作成することもできます。
次に、describe-itメソッドでテストメソッドを実装します。
describe("Nuxt Application", () => { it("Rendering Welcome Page", () => { cy.visit("/"); cy.get("h2").should("include.text", "Welcome to your Nuxt Application"); }); });
DOM要素の取得や操作は、CypressのビルトインAPIを使用します。
また、アサーションはChaiのAPIを用いて記述します。
お気に入りポイント
Cypressを使用してみて、個人的に良いと感じた点(機能)をご紹介します。
TypeScript対応
CypressはTypeScriptに対応しています。
型定義が提供されているだけでなく、TypeScriptで記述したテストコードをJavaScriptにビルドすることなくそのまま実行できます。
(プロジェクト内にTypeScriptをインストールする必要があります)
Custom Commands
Custom Commandsという機能を使うことで、ビルトインAPIを拡張したり、独自のAPIを追加できます。
共通処理をCustom Commandsとして登録することで、テストコードの可読性を向上させることができます。
下記の例では、ログイン処理をCustom Commandsとして登録し、各テストメソッドの実行前にログイン処理を実行しています。
cypress/support/commands.ts
// ログイン処理(IDとパスワードを入力してログインボタンをクリックする) Cypress.Commands.add("login", (id: string, password: string) => { cy.get("input[type='text']").type(id); cy.get("input[type='password']").type(password); cy.get("button").contains("ログイン").click(); });
cypress/support/e2e.ts
import "./commands"; beforeEach(() => { // ログイン画面にアクセス cy.visit("/login"); // テスト用のユーザーアカウントでログインする cy.login("test1", "password"); });
fixture
fixtureという機能を使うことで、プロジェクト内に用意したテストデータを読み込むことができます。
これによって、テストコードとテストデータを分離して管理できます。
下記の例では、ユーザーアカウント情報をテストデータとして作成し、そのデータを使ってログイン処理を実行しています。
cypress/fixtures/users.json
[ { "id": "test1", "password": "password", "admin": true }, { "id": "test2", "password": "password", "admin": false } ]
cypress/support/e2e.ts
require("./commands"); interface User { id: string; password: string; admin: boolean; } beforeEach(() => { // ログイン画面にアクセス cy.visit("/login"); // テスト用の管理者アカウントでログインする cy.fixture("users.json").then((users: User[]) => { const admin = users.find((user) => user.admin); cy.login(admin.id, admin.password); }); });
ハマりポイント
Cypressを使用してみて、少しハマった点をご紹介します。
iframe
テストを実行すると指定したブラウザが起動しますが、テスト対象のアプリケーションの画面はiframe内に描画されます。
テストコードを実装する際は、基本的にビルトインAPIを使用してDOM要素の取得や操作を行うので、iframeを意識することはありません。
しかし、例えばアプリケーションの画面でさらにiframeを使用している場合などは、iframeを意識する必要があります。
つまり、アプリケーション画面内のiframeの読み込みが完了するのを待ち、iframe > 対象の要素
という構造を意識してDOM要素の取得や操作を行います。
working-with-iframes-in-cypress
ドラッグ&ドロップ
Cypressには、ドラッグ&ドロップ操作を行うAPIが提供されていません。
HTML標準のドラッグ&ドロップイベントをトリガーすることで実装できますが、cypress-drag-dropというプラグインを導入すると、より簡潔に実装できます。
ドラッグ&ドロップに限らず、多くのサードパーティ製プラグインが存在するので、困った場合は独自実装する前にプラグインを探すと良いかと思います。
おわりに
以上、簡単ではありますが、Cypressについてご紹介しました。
いかがでしたか?Cypressには本記事で言及した機能以外にもさまざまな機能があります。
また、現在も頻繁に更新されており、今後ますます使いやすくなることが期待されます。
E2Eテストの作成・自動化を検討されている方や、Cypressに興味がある方にとって、本記事の内容が少しでもご参考になれば幸いです。
私たちは同じチームで働いてくれる仲間を探しています。プロダクト開発に興味がある方のご応募をお待ちしています。
執筆:@inamaru.yuki、レビュー:@sato.taichi (Shodoで執筆されました)