こんにちは。BacklogのGit機能の開発を行っているテリーです。
今回はGitチーム(後述する僕の所属するチーム)でAutifyによるリグレッションテストの自動化を進めてみて感じたメリットと工夫したところ、苦戦したところを紹介したいと思います。
目次
スモールチームの規模感とテストの現状
BacklogのGitチーム
長らくBacklogは固定のチームが専任で固定の機能をみるような体制になく、アプリケーションエンジニアが比較的流動的にBacklog全体の機能を担当しており、なんとなく“この人”は“この機能”が得意というような体制でした。
ですが最近のチーム編成により固定のチームが固定の機能を開発するようなフィーチャーチームがいくつかできました。Gitチームはその中で生まれたBacklogのGitの機能についての開発責任を持つチームです。チームメンバーは3人で、そのメンバーでインフラからフロントエンドまでの開発の責務を負っています。
僕は育休から5月に復帰したばかりですが、バックエンドの開発を中心に楽しく過ごしています。
GitチームのE2Eテストの整備状況
現在Gitチームが運用している各サービスでE2Eテストの仕様書を作成・メンテナンス・運用しています。
ブラウザからのアクセス
長くGitを支えてきた開発者のおかげで過去に追加してきた機能に対するテスト仕様書も残っており、全体としてメンテナンスされているテスト仕様書が存在している状態でした。
テストケースを網羅するようなブラウザの画面操作を含むリグレッションテストは今まで全て手動で行われていました。
このほかに自動化されているスモークテストもいくつか運用しています。これらのスモークテストは上記のテスト仕様書を網羅しているわけではなく、一般的なGitコマンドを用いた操作を一通り(例えばcloneやpushなど)を各環境で試すようなものになっていて、毎リリース後に社内のCIサーバー上で実施されます。
今回はBacklogのWeb画面に関するリグレッションテストの自動化を中心に話をしていきます。
現状のリグレッションテスト運用
Git HTTP / Git SSH / Git LFSについてはリポジトリも全く別なサブシステムとしてGitチームが運用しているので大体の影響範囲を開発者が把握可能です。なので広範なリグレッションテストは頻繁には行っていません。広範な影響が想定されるプロジェクトでは実施します。
しかしBacklogの画面についてはモノリシックなアプリケーションとして一つのリポジトリで開発がされているためGitチームが予期しない修正がGitの機能に入ることがあります。そのため定期的にリポジトリを監視するチェックスクリプトを動かして、Git関連のファイルを修正するプルリクエストを他のチームの開発者が作成してないかを自家製の差分検知ツールを使って定期的にチェックしています。
更新のチェックを検知したり、大きめの修正があるとの報告を直接受けたらWeb用のE2Eテスト仕様書のテスト項目を手動で実施しています。
手動のテストの工数は大体2人日で、その度に開発者の工数を捻出します。本番環境でリグレッションが起きた時の方が結果として工数を捻出することになるのでコストは回収できていると思っています。
が、やはりテスト作業自体は退屈でモチベーションの維持が難しく、やる意義があるとはいえスモールチームでこれだけの工数を将来にわたって何度も捻出し続けるのは少し苦しいと考えていたため、リグレッションテストの自動化について考え始めました。
どういうものが欲しかったのか
BacklogのWeb画面にはすでにSeleniumで動くスモークテスト(Gitチームが運用していない)が存在しておりそれをGitのリグレッションテストに対応させることも考えましたが以下の理由でやめました。
大量に作りたい
リグレッションテストの全ての項目を網羅しようとすると実装がかなり膨らむことが想定されました。楽観的に見積もってテストを書くのに2人月くらいかかると思っていましたが、少ない人数の中でこれだけの工数を捻出して自動化する必要があるのかは疑問でした。
安定的に運用したい
現存しているスモークテストはid, classに依存していてテストケースを維持するのが難しいと考えていました。Backlogの画面は他のチームの開発者の修正も入るので安定的にHTMLの構造を維持していくのは現実的ではありませんでした。
簡単に作成したい
上述の「大量に作りたい」とも関連しますが、プログラマでなくても簡単に作れるような状況であればテストケースのメンテナンスを自分以外の人にすぐ任せられると考えました。現状のスモークテストは誰でも簡単に触れるような状況ではありませんでした。
上記の理由で違う方法でE2Eの自動テストを用意する必要がありました。
そんな時にたまたま社内のQAチームがAutifyの導入を進めてくれていました。試しに使ってみると自分の求めていたツールにピッタリだったので早速飛びつき、これならGitチームのリグレッションテストを自動化できると考えました。
Autifyについて
Autify社によって開発されているノーコードのソフトウェアテスト自動化プラットフォームです。Webブラウザにエクステンションを入れることでテストケースを作成する人のブラウザ操作を録画しそれをそのままテストシナリオ(Autifyのテストの単位)として保存することができます。
ブログ内に登場するAutifyの用語について
- ステップ: テストシナリオの中の一つひとつの操作。(例: クリック、アサーション、遷移など)
- テストシナリオ: 「テストケースを構成するもの」で、Autifyではステップの集合体で成り立っています
- テストプラン: テストシナリオをまとめて実行してくれる機能
データの構造的には以下のようになります。
Autifyの導入のメリット
Autifyのおかげで手動で行っていたリグレッションテストを10人日で75%自動化できました。実際にここがよかったというところをあげていきたいと思います。
簡単な操作感
テストシナリオ作成のボタンを押すとシークレットウィンドウが立ち上がり、クリックや文字入力をするだけでテストシナリオが作成できます。プログラミングに精通してない人でもブラウザさえ操作できれば、テストシナリオを作成することができます。以前の自動E2Eテストはプログラミングに精通している必要がありました。しかしノーコードでテストシナリオを作成できるため、開発者に限らず多くの人で運用でき、E2Eテストの陳腐化を防ぎやすくなります。E2Eテストの運用を引き継ぐ際にも直感的なUI/UXがそのハードルを下げてくれます。
各ステップのスクリーンショット
テストシナリオの各ステップにはスクリーンショットが表示されます。スクリーンショットがあるおかげで何の操作をしていたのか思い出しやすくなります。各ステップには名前やコメントをつけることもできますが、スクリーンショットは自動で挿入されるため、より簡単です。
また実行時にも各ステップにスクリーンショットを表示してくれます。テストシナリオが失敗した時その失敗したステップより前で想定と違う挙動になっていることも多いと思います。各ステップのスクリーンショットを一覧して見れるため、どのステップで想定と違う挙動になったのかを追跡しやすくなります。
AIによるスマートなDOM操作
よくある自動E2Eテストツールではidやclassを使い対象のDOMを操作します。しかしこの方法では開発者がHTMLの構造を気にする必要があります。classはデザイン適用のために変更することがあるため頻繁に変わる可能性がありますし、idは操作したいDOMに当たっていなかったりします。これを回避するためにラベルからDOMを操作したり、HTMLの構造の解析から対象のDOMを取得する方法が考えられます。
しかしこれらの複雑なDOM操作をE2Eテストをしたいだけの作成者が用意するのはコストを払いすぎているような気がします。
Autifyの技術ブログによるとAutifyではこれらの複数のロケーター(DOMの操作のためのキー)から対象のDOMをAIによって類推するようなアプローチを使っているようです。これにより、例えばclassが変わってもラベルが同じであれば同じボタンと判定することができます。画面の軽微な変更はAutifyの方でよしなに追随してくれるため、E2Eテスト作成者の負担をかなり軽減することができます。
個人的にいくつか実験をして実際にclass/id/ラベルの変更にある程度追随してくれることを確認しました。
参考)
https://blog.autify.com/ja/how_can_we_improve_the_testability_of_applications
https://blog.autify.com/ja/why-id-should-not-be-used
Autifyによる自動E2Eテスト
Autifyの方がより簡単にテストを作成できます。反面テストケースをコードで表現できた方がより複雑なより豊富な入力パターンのテストを実施しやすいです。
JSステップによる凝った操作の追加
基本的にテストシナリオの作成はブラウザで直接操作をしていくのですが、それ以外にもJavaScriptを埋め込んでアサーションや操作を追加することができます。document.querySelectorを使って特定のDOMが存在するかどうかを確かめたり、HTTPリクエストを飛ばして該当のURLが正しいレスポンスを返却するのを確かめたりすることができます。
Autify社がいろんなJavaScriptのサンプルを用意しているので、これを参考にしつつクリックなどの操作以外の複雑なアサーションなどを追加することができます。
参考: https://github.com/autifyhq/autify-javascript-snippets
ブラウザの操作だけでは対象のリンクをクリックできない場合や、対象のファイルをダウンロードしたり、ランダムな文字列の生成する際にJSステップを使用しました。ただあまり使うと直感的なUIやAutifyの方で抽象化されたDOM操作から離れて複雑で他の人がメンテナンスしにくいテストになってしまうので注意が必要だと感じました。
余談ですがJSステップのサンプルのバグを見つけてAutify社以外の人で初めてのコントリビューターになれました。
https://github.com/autifyhq/autify-javascript-snippets/pull/20
Autify側でメールアドレスを用意できる
Autify側でメールアドレスを発行してくれるので、アカウントを管理しやすくなっています。サービス側でメールアドレス発行がないとテストシナリオ作成者でメールアドレスの管理をしなければならず手間が増えることになります。また実際に存在するユーザーを使ったりするリスクがなくなりました。
詳しくはFAQを参照ください。
Autifyの運用で工夫したこと
実際のテスト仕様書
テストケースにユニークIDの付与
テストケースの追跡性を高めるためにGIT-xx-xx-xxxのように各テストケースにユニークでインクリメンタルなIDを付与しました。インクリメンタルなIDなのでテストケース追加の場合でも簡単に採番を行えます。当たり前ですが必ず今までの数字がずれないように手作業でメンテナンスしなければならないので、今後テストケース追加時には注意しなければなりません。
テスト仕様書とAutifyのテストシナリオの関連付け
テストケースとAutifyのテストシナリオの関連付けは非常に大事です。Autifyの画面をみているだけでは何が行われて何を確認しているのかわからなくなります。なので各テストケースの項目にAutifyのURLを記載するようにしました。Autifyで実現できないテストケースには手動というラベルを振っています。
各テストシナリオにはタイトルとメモが書けるので、そこに各テストケースのケースIDを記載してどのステップがどのテストを行っているのか分かるようにしました。
より大きい参照情報であるAutify用のユーザーやAutifyのURL、テストプランはテスト仕様書の一ページ目に記載して見逃さないようにしました。
Autifyのテストシナリオのステップ数について
テストシナリオの実行数について課金なのでテストシナリオの数はなるべく抑える必要があります。しかしプログラマの習性のせいか各テストシナリオをシンプルにしたく少ないステップ数にとどめようとしてしまいました。結果現在はテストシナリオを作り過ぎてしまったので、今度はテストシナリオの数を抑えるために統合しなければなりません。まだ最適解を見つけていませんが、運用をしながらテストシナリオの数とそのステップ数の最適解を見つけられたらと思います。
テストに利用するデータに固有のIDを付与する
私たちはGitの画面をメンテナンスしているので、Web上から事前に特定のデータを登録する他に、Gitに特定のブランチやタグを用意してAutifyからはそれを利用するようにしました。Autify側にDOM操作を任せるよりデータを用意する手間は必要だけど必ずそのデータを選択できるような仕組みが欲しかったためです。
ずらっと並んだ<li>タグや<a>タグから特定のラベルがあるDOMを操作できるようにJSステップを用意しています。
テスト用の固有のデータの管理には課題があります。何が今使われているのか。いらなくなったデータはないか。などを管理する仕組みが必要だと感じました。
テストプランの並列実行
Autifyでは各テストシナリオを一つのテストプランとして同時に実行することができます。このテストプランは登録されているテストシナリオを並列で実行するため、並列で実行されても耐えられるようなテストシナリオを作成するように意識する必要があります。基本的にはデータを更新する操作は複数テストシナリオで同一のデータを触らないようにします。
具体的には以下です
- 同一のテストシナリオで実行して直列に実行されるようする
- 各々のテストシナリオ内で直列に一つのデータを更新するようにする
更新系
参照系
テストデータのクリーンアップ
何回実行しても同じ結果を得たいので作ったデータは削除する必要があります。(例えばコメント追加など)Autifyには「このステップはエラーとしてテストを続行する」オプションがあり必ず最後にリソースを削除したいテストシナリオには可能な限りこれを付与するようにしています。ステップ毎に付与する必要があるので各ステップに手作業でつけるのが大変なのですが…。
Autifyで困ったこと実現できなかったこと
2021/07時点での話になります。Autifyはこれからもアップデートが続くと思いますので未来もこの限りではないはずです。
ステップグループがテストシナリオ作成時にしか使えない
テストシナリオ作成時にしか使えないのでテストシナリオを超えたステップの使い回しが難しいです。ロードマップには開発予定とあるので期待したいです。
ドラッグ&ドロップのテストが難しい
Autifyレコーダーでは検知できない操作です。JavaScriptでイベントを飛ばすことで実現できそうですが、まだ試していません。
JavaScriptエラーの検知
クリックなどのアクションの後にJavaScriptのエラーが発生した場合には検知したかったのですが、AutifyのJSステップは非同期実行時のエラーのキャッチはサポートしておらず検知することができませんでした。
これからの課題
異なる環境でのデータの整備と管理
1つの環境でのデータの整備を手動で行ってしまったので、異なる環境へのテストの実行する際はBacklogのAPIを使用することで自動化する作業が必要となります。
また現在各テストシナリオやテストケースにどのデータが使われているのかを管理する仕組みが必要だと感じています。
サービスにロックインされてしまう
テスト作成の簡単さはとても魅力的なのですが、反面E2Eテストを他のサービスや自前のSelenuimに移植することが難しい(無理に近い)くなり、Autifyから離れることができなくなります。今は特に問題はないですが今後どうなるかはわかりません。
各テストシナリオが一つのリストに存在する
Autifyの各テストシナリオは階層がなくフラットなので、各テストシナリオのタイトルにGIT-Dev-jaのように[ユニークID-環境-言語] プリフィックスをつけることで対象のテストシナリオを検索によって発見するできるようにしています。テストシナリオ作成者の名前の付け方に依存した仕組みなので、各テストシナリオを構造化された状態で管理できたら嬉しいと感じています。
まとめ
私はこれまでもE2Eテストの自動化に取り組んだことはあるのですが、構築の工数、メンテナンスの負担の重さからリグレッションテストの自動化に成功したことはありませんでした。AutifyではSeleniumの構築などに伴う様々な作業をすっ飛ばして、最初からテストのみに注力できるため、自分の今まで求めていたことを実現できた感じがありました。
スモールチームにとってリグレッションテスト実行の負担は相対的に重くなります。今回の自動化によって、チームがより本質的な価値を届けることに集中することに繋がればと思います。
テスト作成者としての課題はまだまだありますが、今後もリグレッションテストの手動実行の工数の削減と、製品の品質の維持を実現するために少しずつ改善していきたいです。