後輩くんに「E2Eテストなんか怖い😣」と言われたので「ソンナコトナイヨー☀」を体験してもらうために書く。
Table of Contents
Playwright
とりあえず、今回はこのPlaywrightってやつを使ってみよう。 まずは「なんか怖い」を払拭するためだから理由とかは脇においておこう。 でも軽快だし、クロスブラウザできるし、スクリーンショットやビデオも取れるし、いいぞ。
Playwrightをインストール
Docker使ってくぞ。まずは準備だ。
FROM node:19.4ENV WORKDIR=/playwright/
WORKDIR $WORKDIR
version: '3'
services: playwright: build: . volumes: - .:/playwright
docker compose build
インストールは公式ドキュメント見てこう。
$ docker compose run playwright yarn create playwright
? Do you want to use TypeScript or JavaScript?> TypeScript
? Where to put your end-to-end tests?> tests
? Add a GitHub Actions workflow?> N
? Install Playwright browsers (can be done manually via 'yarn playwright install')?> n
? Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo yarn playwright install-deps')?> n
いろいろなディレクトリやファイルが生成されているな。
- node_modules/- tests/ - example.spec.ts- tests-examples/ - demo-todo-app.spec.ts- .gitignore- docker-compose.yml- Dockerfile- package.json- playwright.config.ts- yarn.lock
こんな感じになってるはず。
browsersとoperating system dependenciesはほしいので、Dockerfile
に書いてこう。
FROM node:19.4ENV WORKDIR=/playwright/
WORKDIR $WORKDIR
COPY package.json $WORKDIRCOPY yarn.lock $WORKDIR
RUN yarn installRUN yarn playwright install && yarn playwright install-deps
CMD ["yarn", "playwright", "test"]
Dockerfile
いじったのでもう一度ビルドしておく。
docker compose build
準備はこんなとこでOKかな。
テストを実行してみる
最初からtests/sample.spec.ts
ってサンプルテストコードがあるじゃないか。
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => { await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring. await expect(page).toHaveTitle(/Playwright/);});
test('get started link', async ({ page }) => { await page.goto('https://playwright.dev/');
// Click the get started link. await page.getByRole('link', { name: 'Get started' }).click();
// Expects the URL to contain intro. await expect(page).toHaveURL(/.*intro/);});
test()
ってのが2つある。これがテストケースだな。 その後に'has title'
、'get started link'
ってテキストが付いてるけど、テストケース名だろう。 コメントアウトで説明も書かれているけど、なんかコードからある程度わかりそうだな。
has title
page.goto()
で括弧内のURLにアクセスしてそう。 page.goto('https://playwright.dev/')
は https://playwright.dev/ にアクセスしてるんだろう。
expect(page).xxx
で何かを検証していそうだな。 例えばexpect(page).toHaveTitle(/Playwright/)
は、ページのタイトルに「Playwright」が含まれていることを検証していそうだ。 このページのタイトルは「Fast and reliable end-to-end testing for modern web apps | Playwright」みたいだからパスしそう。
get started link
page.goto('https://playwright.dev/')
はhas title
のテストと同じだ。
page.getByRole('link', { name: 'Get started' }).click()
は、「Get strted」ってテキストを持つリンクをクリックしているんだろう。ページトップにあるボタンだな。
expect(page).toHaveURL(/.*intro/)
でURLがintro
で終わるか見てるな。 実際遷移先のURLはhttps://playwright.dev/docs/intro
だからこれもパスしそう。
これを回してみよう。
$ docker compose up6 passed
パスした。 ちなみにtests/sample.spec.ts
にはtest()
は2つしかないけど6つのテストがパスしてる。 そしてコンソールを眺めてると[chromium]
,[firefox]
,[webkit]
の文字が見えた。 そう、すでにクロスブラウザでテストしてやがる。やりやがる。
この辺は、playwright.config.ts
を眺めるとなんとなくわかる。
...export default defaultConfig({ testDir: './tests', ... projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] }, },
{ name: 'webkit', use: { ...devices['Desktop Safari'] }, },
/* Test against mobile viewports. */ // { // name: 'Mobile Chrome', // use: { ...devices['Pixel 5'] }, // }, // { // name: 'Mobile Safari', // use: { ...devices['iPhone 12'] }, // },
/* Test against branded browsers. */ // { // name: 'Microsoft Edge', // use: { ...devices['Desktop Edge'], channel: 'msedge' }, // }, // { // name: 'Google Chrome', // use: { ..devices['Desktop Chrome'], channel: 'chrome' }, // }, ], ...})
DesktopのChrome、Firefox、Saferiでテストしたようだ。 そしてコメントアウトにさらなる夢を感じる。
テストを書いてみる
自分でもテストを追加してみたい。
公式ドキュメントが丁寧。
https://playwright.dev/ にはページトップに「Playwright enables reliable end-to-end testing for modern web apps.」ってヒーロータイトルがある。これを検証してみよう。
...
test('has hero title', async ({ page }) => { await page.goto('https://playwright.dev/')
await expect(page.getByRole('heading', { name: 'Playwright enables reliable end-to-end testing for modern web apps.' })).toBeVisible()})...
要素はLocatorsというもので指定するらしい(page.getByRole('header', { name: xxx })
のとこ)。 toBeVisible()
で存在していること(見えていること)を検証している。 詳しくは、公式ドキュメントを参照。
ちなみにclass
を使ったりして指定もできる。
...
test('has hero title', async ({ page }) => { await page.goto('https://playwright.dev/')
await expect(page.locator('.hero__title')).toHaveText('Playwright enables reliable end-to-end testing for modern web apps.')})...
$ docker compose up9 passed
どちらの書き方にせよ、9つのテストがパスしたはず。
おぉ...なんかこんな感じでなんだってできそうだな..。
Locatorに対する操作もいろいろ..。
Assertionもいろいろ..。
これ、なんでもできるんじゃないの...??
失敗も見とく
ちゃんと失敗するのかも見ておこう。
...
test('has hero title', async ({ page }) => { await page.goto('https://playwright.dev/')
await expect(page.getByRole('heading', { name: 'Playwright enables reliable end-to-end testing for modern web apps.' })).toBeVisible() await expect(page.getByRole('heading', { name: 'Playwright enables reliable end-to-end testing for modern web apps.' })).not.toBeVisible()})...
.toBeVisible()
の前に.not
をつけた。これで「存在しない(見えない)」ことを検証することになる。 でも実際はあるから失敗するはず。
$ docker compose up3 failed[chromium] › example.spec.ts:20:5 › has hero title[firefox] › example.spec.ts:20:5 › has hero title[webkit] › example.spec.ts:20:5 › has hero title6 passed
3つ失敗してる。期待通り。
ちなみにplaywrightはテストのレポートも作ってくれる。 playwright-report/index.html
というファイルが生成されているので、ブラウザで開いて見てみよう。
うわぁ。見やすい..。
詳細まで見られるんですか..。
失敗時のオプションを少し追加
でも失敗時どうだったのか、このレポートからではわからない。 と思ったらテストオプションがめっちゃ豊富。
import { defineConfig, devices } from '@playwright/test';...export default defineConfig({ testDir: './tests', ... use: { trace: 'on-first-retry', screenshot: { mode: 'only-on-failure', fullPage: true, }, video: 'retain-on-failure', }, ...});
失敗時にスクリーンショットとビデオを撮ることにしてみた。 もう一度テストを失敗させて、レポートを見ると..。
便利すぎ!
怖くないよね?
ということで、怖くなかったよね? むしろめっちゃ味方。頼れるやつやE2E。Playwrightすげぇ。 公式ドキュメント見るとページありすぎて「沼...。今はやめておこう...。」となるレベル。 でもやりたいことはコレくらいの労力で達成できますね。 それでちょっとずつ沼にハマれば、この公式ドキュメントの量は期待感しかない!
ということで、沼にはまろう!
サポートもお待ちしております!