はじめに
今回もtechtrainの課題を参考に作成していきます。
テスト駆動開発の基本サイクルを参考にして、簡単なE2Eテストを作成しました。
ログイン画面の入力フォームにパスワードとメールアドレスを入力して送信ボタンを押すというテストです。
ログイン機能はまだ未実装です。
テスト駆動開発(TDD) とは
テストをしながら開発していく手法の一つです。
「テスト→実装→リファクタリング」を繰り返すことで製作物をより良いものにしていくのが目的である。
基本サイクル
テスト駆動開発のサイクルは「Red・Green・Refactaring」である。
・ Red : 仕様に対して失敗するコードを書く。
・ Green : 成功するコードを書く。
・ Refactaring : 完成したコードをきれいに整理する。
Playwrightとは
Microsoftが開発したテスト自動化ツールである。
今回はこのツールを使用してE2Eテストを行います。
Playwrightインストール
npm init playwright@latest まず最新のPlaywrighをインストールします。
インストール時にいくつかの質問が出てきます。
√ Do you want to use TypeScript or JavaScript?
√ Where to put your end-to-end tests?
√ Add a GitHub Actions workflow? (Y/n)
√ Install Playwright browsers (can be done manually via 'npx playwright install')? (Y/n)
・1つ目の意味は「TypeScriptかJavaScriptのどちらを使用しますか?」という意味です。
・2つ目は、「作成するファイルをどのフォルダーにいれますか?」
e2eにするのが一般的らしい。
・3つ目は、「GitHub Actionsを追加しますか?」
GitHub Actionsを追加するとpushしたときや、 pull Requestをしたときに自動でテストを
実行してくれます。今回はNoにしてみました。
npx playwright create-github-actions これで後からGitHub Actionsを
追加することができます。
・4つ目は 「ブラウザをインストールするかどうか?」
Nを選んだ場合は npx playwright install と打ち込み自分でブラウザをインストールする必要があります。
テスト実行方法
npx playwright test これで実行できますが、学び始めの私には少しわかりずらいので、
今回はnpx playwright test --ui こちらでテストを実行しようと思います。
これは動きを見ながらテストを実行できるため、初心者にはわかりやすいですね。
npx playwright test --uiを使用するために…
npx playwright test --uiでテストを起動したとき、同時にサーバーを起動させるようにします。
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: true,
},
playwright.config.js の最後の方にある項目をこのように変更します。
そうするとnpx playwright test --uiでテスト実行時に自動でnpm run devを実行してくれます。
url: 指定したURLにアクセスする。
reuseExistingServer: true 起動中のサーバーがあればそれを使用してくれる。
ベースURL設定
use: {
/* Base URL to use in actions like `await page.goto('')`. */
// baseURL: 'http://localhost:3000',
baseURL: 'http://localhost:5173',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
ベースのURLを設定しました。これで、page.goto(/login)とかくだけで http://localhost:5173/login にアクセスできるようになります。
テストコード
// @ts-check
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('/login'); //qiita用めも これでtest() 使用時に毎回/loginに飛ぶコードがじっこうされる。
})
test('正しくないメールアドレスを入力した場合', async ({ page }) => {
await page.getByLabel('パスワード').fill('123');
await page.getByLabel('メールアドレス').fill('22');
await page.getByRole('button', { name: '送信' }).click();
await expect(page.getByText('有効なメールアドレスを設定して。')).toBeVisible();
});
test('なにも入力しなかった場合', async ({ page }) => {
await page.getByRole('button', { name: '送信' }).click();
await expect(page.getByText('パスワードを入力してください')).toBeVisible();
await expect(page.getByText('メールアドレスを入力してください')).toBeVisible();
});
test('成功した場合', async ({ page }) => {
await page.getByLabel('パスワード').fill('123');
await page.getByLabel('メールアドレス').fill('test@gmail.com');
await page.getByRole('button', { name: '送信' }).click();
await expect(page.getByText('ログインに成功しました。')).toBeVisible();
})
テストに合わせて作成したログイン画面のコード
import { useState } from 'react';
import { useForm } from 'react-hook-form'
const Login = () => {
const [success, setSuccess] = useState('');
const onsubmit = (data) => {
console.log(data);
setSuccess('ログインに成功しました。');
};
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
return (
<form onSubmit={handleSubmit(onsubmit)} noValidate>
<label htmlFor="password">パスワード</label>
<input id='password'
type="password" {...register('password', {
required: 'パスワードを入力してください',
})} />
{errors.password && <p>{errors.password.message}</p>}
<label htmlFor="email">メールアドレス</label>
<input
id='email'
type='email'
{...register('email', {
required: 'メールアドレスを入力してください',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: '有効なメールアドレスを設定して。'
}
})} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit">送信</button>
{success && <p>{success}</p>}
</form >
);
};
export default Login;
メールアドレスのバリテーションについて。
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
これの意味について解説します。
・/.../i スラッシュ内がチェックするパターンでiが大文字小文字を区別しないという意味
・^と$は初めから最後までという意味
・[A-Z0-9._%+-] AからZ、0から9( . _ % + - )が1文字以上必要
・+@ 次は@が来る
・[A-Z0-9.-] AからZ、0から9、-の中で1文字以上必要
・+\. 次は.が来る
・[A-Z]{2,}AからZの文字が2文字以上必要
重用点
<form onSubmit={handleSubmit(onsubmit)} noValidate>
このように<form/>にnoValidateを入れないとChromeなどの
標準バリテーションが動作してしまい、テストがうまく動作しませんでした。
また、なぜ標準バリテーションが出るかというと、type=emailとしているためです。
結果
テスト 「2なにも入力しなかった場合」
反省点
正直に言うと、テストから作成するのが難しかったため、ログイン画面と同時並行に作成してしまいました。
テストコードを書いてから作成していく、というのがまだあまり想像でません。
実際に開発に携わっていく中で学んでいきたいと思います。
学んだこと
E2Eやテスト駆動開発という存在を知ることができました。仕事をしていく中でもっと理解を深めるように頑張りたいです。
今回はフォーム入力とボタンをクリックするという簡単なテストコードでしたが、書き方を学ぶことができました。
少しずつReactについての理解が深まってきたように感じます。


