どうもこんにちは。
私はアプリケーションの実装に加えて、試験仕様書の作成業務と試験実施業務を担当しています。
試験業務って工数を圧迫しますよね
試験業務って、コーディングに比べたら比較的単純な作業だからすぐ終わるイメージがあると思いますが、実はめっちゃ工数を圧迫してます。(私の場合)
1つのマイルストーン内の業務における、大体の比率はこんな感じです。
| 業務内容 | おおよその割合(%) |
|---|---|
| 基本設計 | 0 |
| 詳細設計 | 5~10 |
| 実装 | 30 ~ 35 |
| 結合・機能試験仕様書作成 | 40 |
| 結合・機能試験実施 | 15 |
| リリース | 5 |
試験仕様書の作成と試験実施で 55% の工数を圧迫しています。
デグレ対策の試験もしなきゃいけない
上記に加えて、 デグレ が発生していないか確認するための回帰試験を実施します。この回帰試験は、以下の観点で試験仕様書作成, 試験実施を行います。
回帰試験観点
最低限、以下の2点かなと思っています。
| 観点 | 説明 | 理由 |
|---|---|---|
| 権限 | デグレが発生せずに全ての権限が想定通りの動作をしているか | ユーザの操作範囲が意図せず変わっていたらダメ |
| 修正によって影響が想定される箇所 | デグレが発生せずに修正前と同じ動作をしているか | ユーザ操作時に挙動が意図せず変わっていたらダメ |
E2Eで自動化したい
修正によって影響が想定される箇所 の試験は、その時の修正内容によって、試験範囲が変動することがあるため、毎マイルストーンで同じE2Eテストを実行することはできません 。
しかし、権限 の試験は、修正内容に関わらず、同一の挙動をしているか確認をする必要のある箇所です。そのため、 毎マイルストーンで同じE2Eテストを実行することができます 。
E2Eテストを構築
今回、私が構築したE2Eテストは以下の技術スタックで構築しました。
| 項目 | 内容 |
|---|---|
| 言語 | TypeScript |
| フレームワーク | Playwright |
E2Eテストプロジェクトの構造
私の作成したE2Eテストプロジェクトの構造は以下のようになっています。
% tree
.
├── playwright.config.ts
├── README.md
├── test-results
│ └── permission_test-管理者テスト-chromium
│ └── trace.zip
└── tests
├── config
│ └── settings.yml
├── helpers
│ ├── auth.ts
│ ├── checklist.ts
│ ├── customer.ts
│ ├── notice.ts
│ ├── question.ts
│ ├── test-data.ts
│ └── user.ts
├── images
│ └── sample_file.png
├── after_release_check.spec.ts
└── permission_test.spec.ts
タイムアウト値
私の場合、全ての権限の試験を一度に実行しようとしているので、かなり時間がかかります。そのため、各テストのタイムアウト値と全テストのタイムアウト値は長めに設定しています。
playwright.config.tsにタイムアウト値を設定しています。
タイムアウト値以外は全てデフォルト値です。
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// ...省略...
timeout: 30 * 60 * 1000, // 各テストのタイムアウト(30分)
globalTimeout: 180 * 60 * 1000 // 全テスト実行の上限(任意)
})
テストファイルの構造
上記のtreeを見ていただければわかるかと思いますが、私はテスト実行ファイルと 機能ごとの処理ファイル(ヘルパーファイル) を分けています。
実行関数から機能ごとの処理関数を呼び出すイメージです。
ヘルパーファイルのインポート
import { test } from '@playwright/test';
// テストデータの生成
import { define_login_info, generateTestData } from './helpers/test-data';
// 各機能のヘルパー
import { login, logout } from './helpers/auth';
// ...省略...
実行環境を変更しながら実行
私は、「どのバージョンでテスト実行したのか」をテストデータに残しておきたかったので、テスト実行前にversion変数にバージョンを格納するようにしています。
また、本番環境で実行する場合、STG環境で実行する場合などを想定して、env変数に環境名を格納するようにしています。
// バージョン
let version = 'test';
// 実行環境
let env = 'local'; // local or dev or stg or pro
envの値によって、URLが変わる仕組みです。
let url: string;
if (env === 'local') {
url = 'http://localhost:3000';
} else if (env === 'dev') {
url = 'https://dev.sample.com';
} else if (env === 'stg') {
url = 'https://staging.sample.com';
} else if (env === 'pro') {
url = 'https://sample.com';
}
テスト実行関数
exec_testという関数で、テスト実行を定義しています。
exec_test関数は、test('管理者テスト', async ({ page })などから呼び出されるようになっています。
async function exec_test(role: string, page: any) {
({ user_name, password, name } = await define_login_info(env, role));
// テストデータを生成
const testData = generateTestData(role, timeStr, version);
const {
// ...省略...
} = testData;
// ログイン
await login(page, url, user_name, password);
// 顧客
await exec_customer(page, role, customer_name);
// よくある質問
await exec_question(page, role, question_name);
// マニュアル
await exec_manual(page, role, manual_name);
// 設定 > ユーザ設定
let user_id = await exec_user(page, role, user_id_for_user, user_name_for_user);
// 設定 > お知らせ設定
await exec_notice(page, role, notice_title, notice_detail);
// 設定 > チェックリスト設定
await exec_checklist(page, role, check_list_name);
// ログアウト
await logout(page, name);
return `テスト完了 (${role})`;
}
test('管理者テスト', async ({ page }) => {
const role = 'admin';
const msg = await exec_test(role, page);
console.log(msg);
});
test('営業テスト', async ({ page }) => {
const role = 'sales';
const msg = await exec_test(role, page);
console.log(msg);
});
E2Eテストの構築にかかった工数
作業は以下の順番で進めました。
- Playwright環境構築
- 大元のテスト実行ファイルの作成
- 各機能のヘルパーファイルの作成
- 各ユーザ権限ごとに実行確認
- 全て通して実行確認
それぞれにかかった工数は以下です。
| 項目 | 値 |
|---|---|
| 1. Playwright環境構築 | 1日 |
| 2. 大元のテスト実行ファイルの作成 | 2日 |
| 3. 各機能のヘルパーファイルの作成 | 5日 |
| 4. 各ユーザ権限ごとに実行確認 | 3日 |
| 5. 全て通して実行確認 | 3日 |
ヘルパーファイルは40ファイル作成しました。
構築時に困ったこと
1. 他の機能のテストで作成したデータを別の場所で使用したい場合
例えば、機能Bの新規登録画面で、機能Aで登録されたデータを使用したいというケースがあったとします。
あるユーザ権限では、機能Aのwrite権限がなくデータが登録できないので、機能Bの新規登録画面で選択ができないというケースが発生しました。
この場合の解決策としては、テストデータにroleの文字列を含めていたため、role部分をsalesからadminに置き換えることで選択ができるようにしました。
2. コード量が膨れ上がる
これはヘルパーファイルを有効活用しました。
また、テスト対象のアプリケーション側に保存されているYAMLファイルを保存して読み込むようにすることで、readがある場合, writeがある場合などの条件分岐を簡単に記述できるようにしました。
さらに、各ユーザ権限を持ったログインユーザも予め作成しておき、ログイン情報をヘルパーファイル内に持たせておくようにしています。
まとめ
なかなか時間がかかりましたが、今後の品質とテスト実行の工数を考えればよくやったなと思います。
以上