Playwrightによるテストを入れてみた

目次

  1. 1. Playwright とは
  2. 2. インストール
    1. 2.1. コマンド
    2. 2.2. オプション選択
      1. 2.2.1. TypeScriptを使うかどうか
      2. 2.2.2. どこにテストコードを置くか
      3. 2.2.3. GitHub Actionsのワークフローを追加するか
      4. 2.2.4. Playwright用ブラウザのインストール
      5. 2.2.5. インストール完了
  3. 3. テスト作成
    1. 3.1. 代表的なロケーター
    2. 3.2. テスト例
  4. 4. テスト実行
    1. 4.1. test
    2. 4.2. show-report
    3. 4.3. GUIで実行
  5. 5. Cypress との比較(当社比)
    1. 5.1. ディレクトリ構成がシンプル
    2. 5.2. DOM操作
    3. 5.3. VSCodeの拡張が充実
  6. 6. 総括
mv

ウチもそろそろテストツールを入れてみようということで、今日はPlaywrightというテストツールの話です。

Playwright とは

Playwright は Microsoft 製のテスティングフレームワークです。
Fast and reliable end-to-end testing for modern web apps | Playwright

以前に Cypress を使ったことはありましたが、個人的には Cypress より使いやすいと感じます。ということで主観ではありますがそんな話をしていこうと思います。

インストール

コマンド

公式のコマンドを実行するだけです。

Installation | Playwright

npm

npm init playwright@latest

yarn

yarn create playwright

オプション選択

いくつか聞かれるので答えていきましょう。デフォルトは以下のとおりです。

TypeScriptを使うかどうか

テストをJSで書くかTSで書くかを選択します。デフォルトはTSを使うようになっています。このへんはさすが Microsoft ですね。

? Do you want to use TypeScript or JavaScript? …
❯ TypeScript
JavaScript

どこにテストコードを置くか

デフォルトは tests ですが場所や名前を変えたい場合は調整してください。とはいえ後からでも変更はきくのでとりあえずデフォルトのまま進んじゃっても大勢に影響はないです。

? Where to put your end-to-end tests? › tests

GitHub Actionsのワークフローを追加するか

このへんも Microsoft 効果なのか、GitHub Actions に寄せたオプションがあります。デフォルトはなしです。これも必要になったらあとから追加すればいいだけなのでインストール時は気にする必要はありません。

? Add a GitHub Actions workflow? (y/N) › false

Playwright用ブラウザのインストール

特に指定がなければデフォルトでは Chromium、Firefox、WebKitが イ ンストールされます。特に理由がない限りは基本デフォルトのまま進めればOKです。

? Install Playwright browsers (can be done manually via 'yarn playwright install')? (Y/n) › true

インストール完了

インストールが完了すると以下のようなメッセージを表示します。

✔ Success! Created a Playwright Test project at /Users/******/******

Inside that directory, you can run several commands:

テスト作成

デフォルトの場合は tests ディレクトリ配下に 名前.spec.ts という名前でファイルを作成すればテスト対象となります。

代表的なロケーター

以下を参考にしてください。
Locators | Playwright

ロケーターはCypressとほぼ同じことが可能です。後発なのでこのあたりは特に機能的に劣ることはまずないでしょう。
以下は Playwright 公式が推奨しているロケーターです。他にもたくさんありますがとりあえず真っ先に出てきた getByRole を載せます。例えばナビゲーションのaboutの場合はこんなかんじです。

await page.getByRole('link', { name: 'about' })) // アクセシビリティ属性によって検索

また、一般的なロケーターは locator で可能です。
これを使うだけでも一応テストは書けますのでとりあえずはこれを使っておくでも良いと思います。

await page.locator('button').click()           // タグ
await page.locator('#reset-button').click() // id
await page.locator('.primary-button').click() // css
await page.locator('//button').click() // XPath

Cypress と同様、Playwright でも公式がベストプラクティスを紹介していますので詳しくはこちらをご覧ください。
Best Practices | Playwright

テスト例

具体的な例としてウチのトップページに入れたテスト内容を一部紹介します。

tests/top.spec.ts

import { test, expect } from '@playwright/test';

test.describe('トップページ', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});

test('GDPR用メッセージ', async({ page }) => {
await expect(page.getByText('すべて拒否')).toBeVisible();
await expect(page.getByText('同意する', { exact: true })).toBeVisible();
await expect(page.getByText('当サイトはCookieを使用しております。Cookie')).toBeVisible();
await expect(page.locator('body')).toContainText('当サイトはCookieを使用しております。Cookieの使用に関する詳細は「プライバシーポリシー」をご覧ください。 Cookieの利用に同意頂ける場合は、「同意する」ボタンを押してください。');

await page.getByText('すべて拒否').click();
await expect(page.getByText('すべて拒否')).not.toBeVisible();
await expect(page.getByText('同意する', { exact: true })).not.toBeVisible();
await expect(page.getByText('当サイトはCookieを使用しております。Cookie')).not.toBeVisible();
});

test('meta情報', async ({ page }) => {
await expect(page.locator('.blog-title')).toHaveText('Jpsern.com');
await expect(page).toHaveTitle('Jpsern.com');
await expect(page.locator('meta[name="description"]')).toHaveAttribute(
"content",
"マンガ、アニメ、ゲームが好きな一応働く気力はあるニート0.5ゲーム差の社会人のサイト。拙いブログとへっぽこ自作音楽置き場です。",
);
});

test('OGP(共通)', async ({ page }) => {
await expect(page.locator('meta[property="og:title"]')).toHaveAttribute(
"content",
"Jpsern.com",
);
await expect(page.locator('meta[property="og:site_name"]')).toHaveAttribute(
"content",
"Jpsern.com",
);
await expect(page.locator('meta[property="og:description"]')).toHaveAttribute(
"content",
"マンガ、アニメ、ゲームが好きな一応働く気力はあるニート0.5ゲーム差の社会人のサイト。拙いブログとへっぽこ自作音楽置き場です。",
);
await expect(page.locator('meta[property="og:url"]')).toHaveAttribute(
"content",
"https://jpsern.com/",
);
await expect(page.locator('meta[property="og:image"]')).toHaveAttribute(
"content",
"https://jpsern.com/images/og.png",
);
await expect(page.locator('meta[property="og:type"]')).toHaveAttribute(
"content",
"website",
);
});

test('OGP(Twitter)', async ({ page }) => {
// see:
// https://developer.twitter.com/ja/docs/tweets/optimize-with-cards/guides/getting-started
await expect(page.locator('meta[name="twitter:card"]')).toHaveAttribute(
"content",
"summary",
);
await expect(page.locator('meta[name="twitter:image"]')).toHaveAttribute(
"content",
"https://jpsern.com/images/og.png",
);
});

test.describe('ナビゲーションメニュー', () => {
test('Home', async ({ page }) => {
const button = await page.locator('text=Home');
await expect(button).toHaveAttribute('href','/');
});

test('About', async ({ page }) => {
await page.click('text=About');
await expect(page).toHaveURL(/\/about\/$/);
await expect(page.locator('h1.article-title')).toHaveText('About サイト案内');
});
test('Archives', async ({ page }) => {
await page.click('text=Archives');
await expect(page).toHaveURL(/\/archives\/$/);
await expect(page.locator('h1.article-title')).toHaveText('Archive Gateway 記事一覧');
});
test('Music', async ({ page }) => {
await page.click('text=Music');
await expect(page).toHaveURL(/\/music\/$/);
await expect(page.locator('h1.article-title')).toHaveText('Music 自作音楽');
});
test('Privacy', async ({ page }) => {
await page.click('text=Privacy');
await expect(page).toHaveURL(/\/privacy\/$/);
await expect(page.locator('h1.article-title')).toHaveText('Privacy Policy プライバシーポリシー');
});
});
});

テスト実行

上記で作ったテストを実行してみます

test

基本は test サブコマンドで実行します。tests 配下の全てのテスト、およびインストールした3ブラウザ全てでテストを実行します。

yarn playwright test

そのため、作ったテストは9つですが結果が27個出ています。テストケース9個 × ブラウザ3つ ということですね。

Running 27 tests using 4 workers
27 passed (15.2s)

To open last HTML report run:

npx playwright show-report

ちなみに実行ブラウザは Chrome だけで良い、って場合は --project=chromium オプションをつければOKです。

yarn playwright test --project=chromium

show-report

結果を確認する際は show-report を使います。

yarn playwright show-report

するとこんなかんじでブラウザでテスト結果を確認できます。
ちなみにテストに失敗した際は自動で show-report が実行されて、その場ですぐ失敗の詳細が表示されます。(今回は全部成功しているので show-report は手動実行してます)

GUIで実行

もちろんGUIでの実行もできます。--ui オプションをつけて実行するとGUIによる実行が行われます。

yarn playwright test --ui

実行すると以下のようなデスクトップアプリが起動します。
▶︎ボタンを押すとテストを実行します。

詳しい説明は公式ドキュメントを参照してください。
UI Mode | Playwright

Cypress との比較(当社比)

あくまでも自分の主観レベルですが、ひとつの意見ということで書いてみます。

ディレクトリ構成がシンプル

慣れの問題と言われればそれまでですが、Cypressはいろいろありすぎてちょっと迷います。tests と test-results の2つで話が終わっているので個人的にはこっちのほうがシンプルでわかりやすいと感じました。

Cypress

e2e
├── README.md
├── cypress
│ ├── downloads
│ ├── fixtures
│ ├── integration
│ ├── plugins
│ ├── screenshots
│ ├── support
│ └── videos
├── cypress.json
├── node_modules
├── package-lock.json
└── package.json

Playwright

e2e
├── node_modules/
├── package-lock.json
├── package.json
├── playwright-report/
├── playwright.config.ts
├── test-results/
└── tests
└── sample.spec.ts

DOM操作

個人的にCypressのあまり好きではない一番のポイントがこれです。
取得したDOMを変数に格納できず、DOM操作にはクロージャが必要でした。これも慣れの問題ではあるんですが自分はいまいちでした。Cypressがダメというよりは単に自分の感覚とは合わないなと思っただけです。

Playwright のロケーターであればこの問題は起きませんでした。CypressもPlaywrightもロケーターが返すのは純粋な Element ではなく独自のオブジェクトではありますが、個人的にはPlaywrightのほうが扱いやすいと感じました。

Cypress

const btn = cy.get("#foo-button")

// これは動かない
btn.click()

Playwright

const btn = page.locator("#foo-button")

// 動く
btn.click()

VSCodeの拡張が充実

Playwright は Microsoft 製ということもあってVSCodeの拡張周りの機能が非常に手厚いです。
Getting started - VS Code | Playwright

中でも特に目玉と思われるのはなんといってもテストジェネレーターじゃないでしょうか。
Test generator | Playwright

テストジェネレータはVSCodeがなくても使えますが、VSCodeの拡張を使ったほうがこのテストジェネレーターがより使いやすいと感じたため、VSCodeユーザーならおすすめします。

事例も豊富にありそうです。下記は適当に拾った記事ですが他にもたくさんヒットします。
イチ押し。Playwrightの快適機能 | フューチャー技術ブログ

総括

Hexoのバージョンアップのたびにサイトが壊れていないかを確認していましたが、このPlaywrightでそのあたりの手間を削減できればいいなあと期待してます。まだ触り始めたばかりなので引き続きいろいろ試してみようと思います。

スポンサーリンク
share