この記事では、Backlog クラシックプランからの移行フローリニューアルの開発プロジェクトで、 Playwright を使いテストデータの作成を自動化したことについて紹介します。
今回、初めて Playwright をさわってみたのですが、複雑な手順をわかりやすいコードとして表現できるのがとても便利だったので知見と今後の課題をまとめます。
目次
要約
- テストデータの作成に時間がかかり、十分に手動テストができていなかった
- Playwright などのツールを使って自動化することで、効率よく不具合を発見できるようになった
- 継続的に E2E テストとしてメンテナンスしてゆくためにはさらに工夫が必要
開発を進める上で感じた課題
仕様変更によって多くのテストデータを用意する必要が発生した
このプロジェクトでは、開発チームが中心となって大まかなフローや画面設計を作っていました。その一方で、画面上の文言やシステムでのデータの表現についてはマーケティングチームやカスタマーサポートチームのメンバーのフィードバックを聞きながら、実装と並行して決めていました。
このフィードバックの中には、開発チームが想定できていなかったパターンが含まれていました。また、ユーザー視点で掘り下げきれていないポイントが見つかって当初想定していたフローや画面設計の仕様を大きく変更する必要がでてきました。
仕様変更によって当初想定してよりも多くのものを実装しなければならなくなり、多くのテストデータを用意しなければなりませんでした。
テストデータの作成に時間がかかって手動テストに時間がかかる
ここで手動テストを行うにあたっての問題を整理しておきます。問題はさまざま考えられますが、ここでは(1)手動テスト手順を間違えてしまうことと(2)解釈が人によって異なりえることに着目してみます。
(1)手順を間違えてしまうことについては、当たりまえですがテストをもう一度やりなおす必要があります。今回のプロジェクトでは Backlog と Nulab Apps の 2 つのシステムで連携する機能を開発しました。手順によっては両方のデータベース(以下 DB )を更新するので、間違った手順で進めてしまうと戻すのに手間がかかります。
(2)解釈が人によって異なりえることについては、テストの再現性を保証できないという問題がありました。具体的に、以前に別の人の手元でうまくいったテストが失敗するようになったとき、問題がシステムにあるのか手順にあるのかわからなくなります。テストが失敗するようになったとき、プログラムと手順の両方を確認しなければならず原因の特定に時間がかかってしまったこともありました。
これらのことから、テストデータを間違いなく解釈をそろえて作るのは非常に負担がかかる作業でした。さらに、テストデータはそのパターンによって増えるため、本来やりたい個々のテストにかける時間が減ってしまったり、全体の作業ボリュームが増えて全部を試すのに膨大な時間がかかるという課題が予想できました。
テストデータ作成の自動化
Playwright によるテストデータ作成の自動化
この課題を解決するために、テストデータを作成する作業を自動化できないかと考えていました。そんなときに、チームメンバーに教えてもらったのが Playwright でした。
Playwright とは Microsoft が中心となって開発している E2E テストフレームワークです。ブラウザーの操作をもとにコードを生成する Test generator の機能が便利で、実際にブラウザーで行った操作を簡単にコードで表現してくれます。次のコードは、Test generator が生成したコードから Backlog にログインする部分を関数に切りだしたものです。
export const login = async(page: any, userId: string, password: string) => { await page.goto(`${SPACE_URL}/`); await page.getByPlaceholder('User ID').fill(userId); await page.getByPlaceholder('Password').fill(password); await page.locator('#submit').click(); }
Test generator で生成されたコードはコマンドラインで実行できますし、Playwright Test for VSCode というプラグインを使うことで VSCode でも実行できます。次の GIF は Playwright Test for VSCode を使ってデバッグ実行しています。
手順をコードにしておいて実行することで、手動テストの手順を間違ってしまったり、人によって解釈が違ってしまったりする課題も解決できます。また、コードは人によるブラウザー上での操作と同等のものですので、実際にユーザーが画面を操作してできたデータと同じものを再現できるというメリットもあります。
もし、何らかの仕様変更があって振舞いが変わったとしても、テストが落ちるようになるのですぐに変化に気付けます。デバッグで実行したときは、ブラウザーの動きを目視で確認できるので、期待しない挙動にも気付くことができました。
これにより十数秒〜数分で、まったく同じテストデータを作成できるようになりました。
DBの初期化と復元
次に、DB の初期化も行えるようにしました。手順を自動化しているとはいえ個々のテストに数分待つのは繰り返しテストを行う上で負担になりました。そのため、DB をすべて初期化してしまうのではなく、あらかじめある時点の dump ファイルをいくつか取得しておいて必要に応じてロードするファイルを切りかえるようにしていました。
やっていることは単純で、 まず、execSync を使って SQL の drop ・ create を実行します。そのあと、データをロードするのですが、呼び出し元から filename を受け取るようにすることで必要な dump ファイルをロードして使いまわせるようにしています。
const dropAndRestore = async (fileName: string) => { const { execSync } = require('child_process'); execSync('mysql -u username -ppassword -e \'drop database backlog\''); execSync('mysql -u username -ppassword -e \'create database backlog\''); execSync(`mysql -u username -ppassword backlog < data/${fileName}`); }
これにより、数分かかるテストデータの作成が十数秒程度で完了するようになりました。
補足
今回は DB の dump ファイルをつかって初期化と復元を実現しましたが、継続的にメンテナンスするのであれば DB マイグレーションツールを使う方法もありました。
というのは、この記事を書くにあたって当時作ったテストを再実行したのですが、ほかのプロジェクトでの UI や DB のスキーマ変更によってテストが通らなくなる問題を修正するのに苦戦してしまいました。
このことから、今後、継続的に機能追加を行うようなプロジェクトで実践する機会があれば、次の 2 つに取り組みたいと思いました。
- CI に組み込んでテスト自体を自動化しすることで UI の変化に気付きやすくする
- DB の変更に追従するしくみを取り入れてメンテナンスコストを下げる
データ作成の自動化と手動テストとのサイクルをまわす
Playwright によるテストと DB の初期化と復元のしくみが整ったら、あとは次のようにステップを繰り返すだけです。
- DB を初期化する
- データをロードする
- Test generator でコードを生成(必要に応じてコードを修正)
- DB の dump ファイルを取得して data/ ディレクトリに配備
- DB の初期化に戻って取得した dump ファイルをロードする
- …
Test generator で生成されたコードは基本的にはそのままテストコードとして使えました。ただ、たまに正常に動作しないことがあり、そのようなときは waitForTimeout を入れたりコードを書きなおしたりする必要はありました。
また、再利用可能な手順は関数に切り出すようなことも試してみました(上の方で示したログイン部分の関数のようなイメージ)。これによって、テストが落ちるようになっても問題の処理にあたる関数だけを修正すればテストが通るよう工夫しました。ただ、この点については、E2E テストの文脈ではテストの共通化はしないというようなドキュメントも目にしたので、結果としてどちらがよかったのかは検証できませんでした。
まとめ
テストデータ作成の自動化の取り組みについて紹介しました。この課題に取り組む以前は、テストデータを作成するという発想はなかったのですが、やってみると効率よくバグを発見でき、想像以上の効果があったと感じました。
何より、一度ベースとなるコードを書いておけば、次からはボタンをクリックするだけでよくなりますし、操作ミスを恐れる必要がなくなったのは大きなメリットだと感じました。結果としてテストの試行回数が増えるだけでなく、テストパターンの抜け漏れを見つける好循環が生まれたと感じています。
一方で、作り込みに十分に時間をかけられなかったので、E2E テストとして開発プロセスの中に組み込むところまでできなかったことは、今後の課題と考えています。