Playwright を使用した E2E テスト (feat. Cypress)
はじめに
こんにちは、クラウドエース フロントエンドディビジョン所属の金です。
本記事では End to End テスト (E2E テスト) のツールの一つである Playwright について基本的な機能を中心にご紹介します。
本記事の対象者
- E2E テストの初心者
- Playwright を軽く試してみたい人
End to End (E2E) テストとは
Playwright のご紹介を始める前に、E2E テストについて簡単にご説明します。
E2E テストは、アプリケーションが意図通りに動作するかどうかをテストするためのソフトウェアテスト技術です。
ご参考: E2E (エンドツーエンド) テストとは? | CircleCI
フロントエンドの E2E テストとは
フロントエンドの E2E テストは、主にブラウザ上での動作を検証します。
サービスによって異なりますが、一般的には以下の項目がテストされます。
- ユーザーがアプリケーションを使用するときに、アプリケーション上でテキストが正しく表示されるかどうか
- ユーザーがボタンをクリックしたときに、アプリケーションが期待通りの動作をするかどうか
- ユーザーが入力フィールドに値を入力したときに、期待通りに機能するかどうか
- クロスブラウザーテスト
- クロスブラウザーテストは、開発したウェブサイトやウェブアプリケーションが広く使用されている主要なウェブブラウザー上で正しく動作するかどうかを確認するためのテストです。
ご参考: はじめてのクロスブラウザーテスト - ウェブ開発を学ぶ | MDN
- クロスブラウザーテストは、開発したウェブサイトやウェブアプリケーションが広く使用されている主要なウェブブラウザー上で正しく動作するかどうかを確認するためのテストです。
Playwright とは
今回ご紹介する Playwright は、Microsoft で開発およびメンテナンスが行われている、Node.js ベースの E2E テスト自動化フレームワークです。
同様のツールには、Cypress や Selenium などがあります。
Playwright 公式ドキュメントの原文より:
Playwright framework is an open-source, Nodejs-based automation framework for end-to-end testing. It was developed and maintained by Microsoft. Its main goal is to run across the major browser engines – Chromium, Webkit, and Firefox.
ご参考:
Playwright の特徴
Playwright の特長は以下の通りです。
- Chromium や WebKit、Firefox など、最新のレンダリングエンジンをサポート
※ レガシー版の Microsoft Edge と IE11 はサポート対象外 - モバイル用ブラウザのテストをサポート
- Android 向けの Google Chrome や iOS 用の Safari のテストも可能
-
npx playwright test --ui
から立ち上がった GUI 画面から直接テストコードを確認することが可能
- テストのスクリーンショットが残るため、対象となるテスト画面の前後の状態を簡単に確認することが可能
- テストの結果を自動的に整理することが可能
- GitHub Actions や Google Cloud Build など、数多くの CI ツールをサポート
- Visual Studio Code(以下、VS Code)用の公式の拡張機能が提供されており、エディタ上でテストを実行することが可能
他にも、テストタスクを実行する前に、要素が実行可能になるまでは、自動的にテストを待機させることができます。
例えば、Playwright を使用して任意のアプリケーションでテストを行う場合、テスト対象のボタンをクリックすると Playwright はセレクタが DOM に表示されるまで待機したり、要素が可視になるまで待機します。
アニメーションが停止するまで待つことも可能で、他のテストツールと異なり、要素が実行可能になるまで待機するためのコードを書く必要はありません。
UI テスト実行例
UI テスト画面
VS Code でテストコード確認ができます。
Playwright の使い方
続いて、簡単な React アプリケーションを作成して、Playwright で E2E テストを実施する方法をご紹介します。
インストール方法の詳細については、以下の公式ドキュメントをご参考ください。
ご参考: Installation | Playwright
インストールは、以下のように作業ディレクトリ配下で実施します。
$ # npm を使う場合
$ npm init playwright@latest
$ # yarn を使う場合
$ yarn create playwright
$ # pnpm を使う場合
$ pnpm create playwright
インストール後、以下のようなファイルが作成されます。
playwright.config.ts
tests/
example.spec.ts
テスト実行コマンド
$ # ディレクトリ配下の全てにテストを実行するコマンド
$ npx playwright test
$ # UI modeでテストを実行するコマンド
$ npx playwright test --ui
Playwright のテストコードの書き方 (基本)
このセクションでは、よく使われている基本的なメソッドについてご紹介します。
詳細なテストコードの書き方については、以下の公式ドキュメントをご参考にしてください。
ご参考: Locators | Playwright
ほとんどのテストは、ページへの URL 移動から始まります。
その後、以下のようなメソッドを使用してページやフレームでアクションを実行する要素ロケータが返されます。
page.locator
1. // css= or xpath= prefix のように prefix で要素指定
await page.locator("css=button").click(); // css の場合
await page.locator("xpath=//button"); // xpath の場合
await page.locator(".className").click(); // class の場合
await page.locator("#id").click(); // id の場合
await page
.locator(
"#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input"
)
.click();
await page
.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')
.click();
page.getByTestId
2. // dataset で要素を選択
<button data-testid="login-button">login</button>;
await page.getByTestId("login-button").click();
page.getByRole
3. <h3>Sign up</h3>
<label>
<input type="checkbox" /> Subscribe
</label>
<br/>
<button>Submit</button>
// 以下のようにimplicit roleを指定して使用
await expect(page.getByRole('heading', { name: 'Sign up' })).toBeVisible();
await page.getByRole('checkbox', { name: 'Subscribe' }).check();
await page.getByRole('button', { name: /submit/i }).click();
page.getByText
4. // 色んな要素がテキストを持ってる場合 locator.filter で必要な要素のみフィルタリング可能
<div>Hello<span>world</span></div>
<div>Hello</div>
// Matches <span>
page.getByText('world')
// Matches first <div>
page.getByText('Hello world')
// Matches second <div>
page.getByText('Hello', { exact: true })
// Matches both <div>s
page.getByText(/Hello/)
// Matches second <div>
page.getByText(/^hello$/i)
locator.filter
5. // 以下のように row の中で必要な要素だけフィルタリングも可能
const rowLocator = page.locator("tr");
// ...
await rowLocator
.filter({ hasText: "text in column 1" })
.filter({ has: page.getByRole("button", { name: "column 2 button" }) })
.screenshot();
Playwright でテストコードを作成 (実践)
React で簡易的なブログアプリを用意し、以下のようなシナリオで実際のテストコードを作成します。
- シナリオ:
- ブログアプリの Home ページでは投稿されてる記事や作成者を確認できる
- 記事をクリックすると詳細画面に遷移できる
- ブラウザの戻るボタンをクリックすると Home ページに戻ることができる
- 画面右上部のリンクをクリックすると該当のページに遷移できる
上記のシナリオに応じたテストコードは以下のようになります。
import { test, expect } from "@playwright/test";
test("Home ページレンダリンテスト", async ({ page }) => {
// Home ページにアクセス
await page.goto("http://localhost:3000");
// 'All'タブがアクティブになっていることを確認
await expect(page.locator(".post__navigation--active")).toHaveText("All");
// 'All'タブをクリック該当ページに遷移
await page.locator(".post__navigation--active").click();
// 各ページに遷移したことを確認
const postLinks = await page.locator(".post__box a").all();
// 各 post のリンクをクリックして、post ページに遷移
for (const link of postLinks) {
await link.click();
// post ページに遷移したことを確認
await expect(page.locator(".post__title")).toHaveText(
"Lorem ipsum dolor sit amet"
);
// ブラウザの戻るボタンをクリックして、Home ページに戻る
await page.goBack();
}
// Footer のリンクをクリックして、該当ページに遷移
await page.locator('footer a:has-text ("write")').click();
await expect(page).toHaveURL("http://localhost:3000/posts/new");
await page.goBack();
await page.locator('footer a:has-text("posts")').click();
await expect(page).toHaveURL("http://localhost:3000/posts");
await page.goBack();
await page.locator('footer a:has-text("profile")').click();
await expect(page).toHaveURL("http://localhost:3000/profile");
});
test("PostDetail ページレンダリン", async ({ page }) => {
// PostDetail ページにアクセス
await page.goto("http://localhost:3000/posts/0");
// Expect post__detail クラス名を持つ要素が存在することを確認
const postDetailElement = await page.locator(".post__detail").first();
expect(await postDetailElement.isVisible()).toBe(true);
// post__title クラス名を持つ要素を取得
const postTitleElement = await page.locator(".post__title").first();
// post__author-name クラス名を持つ要素が存在することを確認
expect(await postTitleElement.isVisible()).toBe(true);
// post__author-name クラス名を持つ要素を取得
const authorNameElement = await page.locator(".post__author-name").first();
// post__author-name クラス名を持つ要素が存在することを確認
expect(await authorNameElement.isVisible()).toBe(true);
});
test("Post タイトルと作成者確認", async ({ page }) => {
// PostDetail ページにアクセス add
await page.goto("http://localhost:3000/posts/0");
// posto__title クラス名を持つ要素のテキストを取得
const postTitle = await page.textContent(".post__title");
// post__author-name クラス名を持つ要素のテキストを取得
const authorName = await page.textContent(".post__author-name");
// post__title クラス名を持つ要素のテキストが正しいことを確認
expect(postTitle).toContain("Lorem ipsum dolor sit amet");
expect(authorName).toContain("kim");
});
Playwright と GitHub Actions でテストを自動化
公式ドキュメントに CI テスト自動化について詳しく記載してあるので以下をご参考ください。
ご参考: CI GitHub Actions | Playwright
設定ファイルは、以下のように YAML 形式で記述します。
name: Playwright Tests #タイトル名を作成
on:
# 指定したブランチで PR & PUSH イベントが発生したら 自動で実行
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
# 実行環境設定
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
# npm package インストール
- name: Install dependencies
run: npm ci
# 以下のように env ファイルを指定できる(下の process.env.CI の例を参考)
env:
CI: true
# Playwright E2E test で使う Browser を設定
- name: Install Playwright Browsers
run: npx playwright install --with-deps
# Playwright E2E test 実行
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
playwright.config.ts
ファイルに以下のように設定することでテスト実行の分岐処理も可能です。
process.env.CI
の例
/* Run your local dev server before starting the tests */
webServer: process.env.CI
? [
{ // web server
command: 'npm run dev',
port: 3000,
},
{ // proxy server
command: 'cd proxy-server && npm run dev',
port: 8080,
},
]
: undefined,
Playwright 用の VS Code 拡張機能
Microsoft が提供している VS Code 用の拡張機能を追加すると、VS Code 上から直接 Playwright のテストを実行できるようになります。
以下のように Record at cursor をクリックすると、実際のブラウザでの操作がリアルタイムでテストコードに反映されます。
Playwright と Cypress の比較
最後に、Playwright と Cypress と比較してみました。
基準 (Criteria) | Platwright | Cypress |
---|---|---|
対応言語 | JavaScript, Java, Python, and .NET C# | JavaScript |
対応 Test Runner Frameworks | Mocha, Jest, Jasmine | Mocha |
対応 OS | Windows, Linux, and macOS | Windows, Linux, and macOS 10.9 ~ |
Open Source | Open Source and Free | Open Source and Free |
Architecture | Headless Browser with event-driven architecture | Executes test cases directly inside the browser |
対応 Browsers | Chromium, Firefox, and WebKit | Chrome, Firefox, and Edge |
iFrame 対応 | あり | 制限 (chromeWebSecurity =false を指定)、出来ない case もあり |
並列処理 (Parallel run) | あり | 課金 (Paid dashboard)か free plugin を使用 |
Mobile testing 対応 | Only view for Android | Only view for website |
CI/CD | あり | あり |
Automatic waiting | あり | あり |
Screenshots / Video 対応 | Screenshots / Video あり | Screenshots のみ |
まとめ
本記事では、E2E テストツールの一つである Playwright の使い方や Cypress との違いについて簡単にご紹介しました。Playwright か Cypress の導入をご検討中の方はの導入をご検討中の方は、以下のまとめをご参考にしていただければと思います。
- Playwright:
- DevTools Protocols を使用して WebKit と Chromium をテストする必要がある場合は、Playwright が最適。
- スクリーンレコーディング、ビデオキャプチャ、tracer viewer などの機能が必要な場合にも Playwright が最適。(Cypress は提供していない)
- テストスピードに関しては Playwright の方が速い。
- Cypress:
- インストールや使いやすさを求める初心者には最適。
- ネットワークキャプチャやビデオ録画などの完全なテスト証拠は提供されていないが、スクリーンショットだけが必要であれば良いツール。
Discussion