react+viteをplaywright+mswで自動テストする
掲題の構成を試してみました。
環境
- react 18.0.0
- react-dom 18.0.0
- react-router-dom 6.3.0
- typescript 4.6.3
- msw 0.42.3
- vite 2.9.9
セットアップ
プロジェクト作成
vite initを使ってプロジェクトを作成します。
npm create vite@latest react-vite-playwright-msw-sample -- --template react-ts
できたプロジェクトに入って追加の依存関係をインストールします。
cd react-vite-playwright-msw-sample
npm i react-router-dom
npm i -D @playwright/test msw
npx playwright install
サンプルのユーザ登録フォームを用意
テスト対象のUIが欲しいので、サンプルのユーザ登録フォームを用意します。
import { FC, useState } from 'react'
import { useNavigate } from 'react-router-dom'
export const RegisterUser: FC = () => {
const navigate = useNavigate()
const [email, setEmail] = useState('')
const [name, setName] = useState('')
return (
<form
onSubmit={(e) => {
e.preventDefault()
;(async () => {
// APIを叩き、ユーザ情報を保存する
await fetch('http://localhost:8000/api/users', {
method: 'POST',
body: JSON.stringify({
email,
name,
}),
headers: {
'Content-Type': 'application/json',
},
})
// マイページに遷移する
navigate('/my-page')
})()
}}
>
<input
type="email"
name="email"
onChange={(e) => setEmail(e.target.value)}
data-test="emailInput"
/>
<input
type="name"
name="name"
onChange={(e) => setName(e.target.value)}
data-test="nameInput"
/>
<button type="submit" data-test="submitButton">
Submit
</button>
</form>
)
}
APIの http://localhost:8000/api/users
をPOSTで叩くとユーザ登録できるという想定です。APIは後でmswでモックします。
ユーザ登録後に遷移する想定のマイページも用意しておきます。
import { FC } from 'react'
export const MyPage: FC = () => {
return (
<div data-test="myPagePage">
<h1>My Page</h1>
</div>
)
}
react-routerによるルーティング設定も行っておきます。
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import { RegisterUser } from './register-user'
import { Registered } from './registered'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/register-user" element={<RegisterUser />} />
<Route path="/my-page" element={<MyPage />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
)
npm run dev
を実行し、ブラウザで http://localhost:3000/register-user
を開くとフォームを確認できます。
playwrightを設定
playwrightの設定ファイルを作成します。どのような設定をするかは好みや状況で変わってきますが、とりあえず以下で進めます。
import { devices, PlaywrightTestConfig } from '@playwright/test'
const config: PlaywrightTestConfig = {
// できるだけ並列にテストを行う
fullyParallel: true,
// マルチブラウザテストを行う
projects: [
{
name: 'Pixel 4',
use: {
browserName: 'chromium',
...devices['Pixel 4'],
},
},
{
name: 'iPhone 11',
use: {
browserName: 'webkit',
...devices['iPhone 11'],
},
},
],
}
export default config
mswを設定
mswの設定を行います。まず以下のinitコマンドを実行し、 public/mockServiceWorker.js
を作成します。このファイルは何も触らなくてOK。
npx msw init public
そして以下のmswモック周りの初期化関数を用意しました。 http://localhost:8000/api/users
をモックするmswワーカを生成します。
import { setupWorker, rest, SetupWorkerApi } from 'msw'
export const buildMswWorker = (): SetupWorkerApi => {
const worker = setupWorker(
rest.post('http://localhost:8000/api/users', (_req, res, ctx) => {
return res(
ctx.json({
message: 'Success.',
})
)
})
)
return worker
}
main.tsxで読み込むようにします。VITEの環境変数として VITE_MOCKED_API
を用意し、それが 'true'
だった時にmswモックを有効化するようにしました。また、dynamic importすることでmswを使用しない場合にバンドルに含まれないようにします。
+;(async () => {
+ if (import.meta.env.VITE_MOCKED_API === 'true') {
+ const { buildMswWorker } = await import('./testing-util/msw-util')
+ const worker = buildMswWorker()
+ await worker.start()
+ }
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<Form />} />
<Route path="/registered" element={<Registered />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
)
+})()
mswを有効化して起動するには
VITE_MOCKED_API=true npm run dev
としてコマンドを実行します。
ブラウザDevToolsのコンソールに
[MSW] Mocking enabled.
と表示されていればOKです。
テストを書く
以下のテストを書き、MSW有効の開発サーバが起動している状態で実行します。
import { expect, test } from '@playwright/test'
test('ユーザ登録する', async ({ page }) => {
// 登録ページを開く
await page.goto('http://localhost:3000/register-user')
// フォームの入力欄を埋める
await page.fill('[data-test=emailInput]', 'foo@example.com')
await page.fill('[data-test=nameInput]', 'foo')
// フォーム送信ボタンを押下
await page.click('[data-test=submitButton]')
// マイページに遷移したことを確認
await expect(page.locator('[data-test=myPagePage]')).toHaveCount(1)
})
以下のコマンドでテストを実行できます。
npx playwright test
上のコマンドはヘッドレスブラウザモードです。ブラウザを目視しながらテストを確認したい場合は --headed
オプションを付与します。またその場合は並列実行よりもシングルワーカ実行の方が確認しやすいでしょう。 --workers 1
も付与します。
npx playwright test --headed --workers 1
テストが意図通りに動かずデバッグしたい場合もあると思います。その場合は await page.pause()
が便利です。
test('ユーザ登録する', async ({ page }) => {
// 登録ページを開く
await page.goto('http://localhost:3000/register-user')
+ // ここでストップする
+ await page.pause()
また、特定のテストだけ実行したい場合は test.only
が使えます。
-test('ユーザ登録する', async ({ page }) => {
+test.only('ユーザ登録する', async ({ page }) => {
// 登録ページを開く
await page.goto('http://localhost:3000/register-user')
Discussion