技術本部 Quality Assurance グループの杉本です。元々はプロダクト開発を行っていましたが、QAグループに SET(Software Engineer in Test)チームが立ち上がるということで異動してきました。
チーム発足から、Playwrightを使ったEnd to Endテスト(以下、E2Eテスト)の導入と最初の成果についてご紹介します。
目的と目標
SETチームが発足したのは、今から約1年前の2023年6月です。私ともう1人、同じプロダクト開発を行っていた平田さんの2人で始まりました。
当初の目的は、リリースして間もなかった、月次決算を加速する「Bill Oneビジネスカード」のリグレッションテストを自動化するというものでした。修正や機能追加のサイクルが早く、手動でのテストはどうしても時間がかかってしまい、リリースサイクルに追いつかない、開発へのフィードバックが遅くなってしまうという問題がありました。
QAグループではすでに Autify、mabl、MagicPodを利用したE2Eテストが行われていましたが、SETチームではE2Eテストフレームワーク(Playwright)を使って進めることにしました。E2Eテストをエンジニアもできた方が良いという考えからです。Autify、mabl、MagicPodのようなノーコード/ローコードツールでテストを作るのは簡単です。プログラミングなどのスキルを必要としないメリットもあります。しかし、プログラミングできるエンジニアにとっては、ツールよりもコードに慣れ親しんでおり、ノーコード/ローコードツールよりも学習コストが低く済みます。プロダクトコードとE2Eテストが同じリポジトリにあることで、変更箇所の発見が容易になるなどのメリットも期待できます。
目的と方向性は決まったので更に深掘りしました。
SETとして何をしたいのか、SETとして何を期待されるのかを突き詰めていきました。Job Descriptionというドキュメントを作成し、自分たちの理念を明確にしました。更に、具体的にやること、そのために準備すること、スケールする方法なども考えていきました。
その結果、2つの新たな目標が生まれました。目的としていたBill Oneビジネスカードだけでなく、すべてのプロダクトに広めていくことです。エンジニアが異なるプロダクトを担当しても、同じようなE2Eテストがある状態が望ましいと定義し、啓蒙活動も進めることにしました。
もう1つは、JSTQB(Japan Software Testing Qualifications Board)のFL(Foundation Level)を第1四半期以内に取得すること。これは「QAとして最低限の知識は保有しておこう、しかもなるべく早く」という考えからです。
E2Eテストフレームワークの選定
上にも記載した通り、E2EテストフレームワークはPlaywrightを採用しました。Selenium、Cypress、Puppeteerそれぞれで実際に実装して決めました。採用理由は以下の通りです。
・無償
コストがかからず、スケールさせやすい。
・制限が少ない
言語縛りや、特定のプラグインしか使用できない、同一オリジンしかできないといった事象がない。
・自動待機機能
◯秒待機の処理を入れなくて済むため、実装容易性が高い、実行時間が短いなどメリットは大きい。
デメリットを1つ挙げるとすると、比較的新しいので実装方法が変わりやすいということでしょうか。初期リリースの頃に比べると実装方法は結構変わっているようです。また、使っていたメソッドがdeprecated(非推奨)になったこともありました。技術ブログを参考にしたけど動かなかったなどもあり得そうです。
Page Object Modelの採用
E2Eテストでよく用いられるデザインパターンのPage Object Model(以下、POM)を採用しました。POMは、テスト対象アプリケーションの画面を1つのオブジェクトとして定義し、テストアクションのインターフェースとして機能します。以下のサンプルコードを参照ください。
POMを利用しない記述
test('メッセージ送信テスト', async ({ page }) => { await page.getByLabel('メッセージ').fill('Hello E2E'); await page.getByRole('button', { name: '送信する' }).click(); });
POMを利用した記述
class MessagePage { readonly message = this.page.getByLabel('メッセージ'); readonly sendButton = this.page.getByRole('button', { name: '送信する' }); constructor(private readonly page: Page) {} } test('メッセージ送信テスト', async ({ page }) => { const messagePage = new MessagePage(page); await messagePage.message.fill('Hello E2E'); await messagePage.sendButton.click(); });
想定するメリットは以下の通りです。
・デザインと操作の分離
デザインの影響範囲をPageObjectに閉じられる。
・再利用性
一度PageObjectを作成したら使い回しが効く。デザイン変更があった場合にPageObjectの修正だけで終わることがある。
逆にデメリットもあります。通常の開発と同じように、共通部分を作成するとどうしてもルールを設けないといけなくなります。PageObjectは画面と1対1にするのか、PageObject自身に持たせて良いメソッドの定義は何か、などなど。本来の主目的である「エンジニアにE2Eテストを書いてほしい」とするのであればスケールしやすいようにルールの少ない運用、つまりPOMを利用しない方が望ましいのかとも考えました。ただ主目的ではあれど、道のりはまだまだ遠いと考えているため、近い未来でメリットを活かせた方が良いと判断しました。
また、デザインをPageObjectに閉じているとは言え、デザインの変更と操作の変更がいっしょになるケースはよくあります。この場合は操作側の修正も必要にはなりますが、操作側にデザインがあるよりは可読性が高く編集も容易だと考えます。
成果
SETチーム発足後の第1四半期でBill OneビジネスカードのE2Eテスト自動化を完了させることができ、従来は、2人で半日から1日かかっていたテストが15分で終えられるようになりました。また、追加目標にかかげたJSTQB FLの取得も、2人とも無事に第1四半期内で取得できました。
実行コスト面での成果はもちろんですが、副産物として以下のものを実感しました。
・リリースの切り戻しや再アップデートなどがあっても常に全件実施が可能となった
・時間に余裕が生まれたため複数ブラウザテストを頻繁に行えるようになった
・自信を持って前と仕様が異なると言い切れる
エンジニアと話すときに、前はこうだったような、、と曖昧な会話になる場合があります。実装したエンジニアであっても確信が持てない場合もあります。E2Eテストは画面仕様書に近い意味を持つため論点が明確になる場合があります。
・E2Eテストを実装しているときに、仕様や実装の不備を見つけることがある
終わりに
まったく新しい取り組みであったため、1スプリント実装するごとに、考えを改めたり、新しい知見を得たりして、実装方法もどんどん変わっていきました。そのため、当初はカバレッジの伸び悩みがありましたが、少人数であったこと、POMを採用したことで、スピード感のある開発が行えたと思います。成果としても上々だと思っています。
今までのキャリアの中でE2Eテストに関わることはありましたが、ここまで向き合ったことはなかったので良い経験になりました。今回はE2Eテストに特化した内容でしたが、SETでは「技術を用いたプロダクト品質保証」を目指しています。ご一緒していただける仲間を募集中です。まずはカジュアル面談からいかがでしょうか。