記事内では特に感動した 3 点についてのみ触れます。また、私の E2E テストに関する経験が乏しい (Cypress を少し触ったくらい) ので、何か誤りがあればご指摘いただけると助かります。
React Testing Library と類似点が多い
私は React コンポーネントのテストを書くときに、React Testing Library を使います。このライブラリは「テストの内容がユーザーの体験に近づくほど、テストの堅牢性も向上する」という思想でつくられており、Playwright と DOM 取得に関するベストプラクティスが一致しています。
About Queries | Testing LibraryOverviewtesting-library.com
Best Practices | PlaywrightIntroductionplaywright.dev
また、Jest によく似た構文を採用していることから、テストの書き心地も非常に近いです。
実際に比較してみると、そっくりですね 😲
// React Testing Library
import { render, screen } from '@testing-library/react';
import { Todos } from '.';
describe('Todos', () => {
test('should render an input to enter a todo title', () => {
render(<Todos />);
const input = screen.getByRole('textbox', { name: 'todo-title' });
expect(input).toBeInTheDocument();
});
});
// Playwright
import { expect, test } from '@playwright/test';
test.describe('Todos', () => {
test('allows us to enter a todo title', async ({ page }) => {
await page.goto('/');
const input = page.getByRole('textbox', { name: 'todo-title' });
await expect(input).toBeVisible();
});
});
両ライブラリの構文が似通っているということは、単に導入の敷居が下がるだけではありません。UI のテスト (単体、結合、E2E) において、一貫したポリシーを守ることに繋がります。その結果、テスト間で脳内のコンテキストを切り替える負荷が減るため開発効率が向上するはずです。
例えば、Cypress では DOM の検索に data-testid
のようなカスタムデータ属性の利用を推奨しています。その考え方 (UI は変わりやすいため、テスト用の ID を付与することで壊れにくいテストになる) にも賛同できますが、やはり思想や原則が一貫しているメリットは大きいと思います。
VSCode と親和性が高い
Playwright は開発元が Microsoft であることから、VSCode との相性が非常によいです。拡張機能をインストールすることで、テストの実行やデバッグを VSCode 上で行えるようになります。また、Jest の拡張機能と組み合わせることで、すべてのテストがツリー上に表示され見通しがよくなります。
さらに特筆すべきは、慣れ親しんでいる VSCode のショートカットやデバッグ手法がそのまま使えることでしょう。以下の画面ではブレークポイントを切り口としたいくつかの機能 (変数のウォッチ、デバッグコンソールなど) を使っています。
機能面が充実している
クロスブラウザの対応をはじめとして、Playwright はテストを支える充実した機能を提供してくれます。1 つ例を挙げると、クライアントサイドのデータ取得に用いる SWRには、タブ切替時にキャッシュを revalidate (再検証) する機能があります。この挙動をテストに組み込みたいとすると、マルチタブに対応していない Cypress では実現できません。
一方で、Playwright では以下のようなテストを書けます。
(テスト対象のページでは、現在時刻を返す API を叩いています)
test.describe('マルチタブのテスト', () => {
test('ユーザーが別タブから移動した場合、現在時刻のキャッシュを再検証する。', async ({
page,
context,
}) => {
await page.goto('/');
const isoDateRegex = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])/;
const currentTime = page.getByText(isoDateRegex);
// 現在時刻を表示する
await expect(currentTime).toBeVisible();
const currentTimeWhenFirstVisit = await currentTime.textContent();
// ユーザーが別タブを開く
const newTab = await context.newPage();
await newTab.goto('https://example.com');
// ユーザーが元のタブに戻ると、現在時刻を再取得する
await page.bringToFront();
const currentTimeWhenComingBack = await currentTime.textContent();
expect(currentTimeWhenFirstVisit).toBe(currentTimeWhenComingBack);
// ユーザーがページをリロードすると、再取得した現在時刻を表示する
await page.reload();
const currentTimeAfterReloading = await currentTime.textContent();
expect(currentTimeWhenComingBack).not.toBe(currentTimeAfterReloading);
});
});
おわりに
Playwright は導入が容易で、使いやすい印象を受けました。これから、E2E テストを書くのがが楽しくなりそうです。