承認テスト(Approval Testing)は、現在のコードの出力を、“承認済”バージョンのものと比較するテスト技術だ。承認済バージョンは、事前にテスト出力を調査して、その結果を承認することによって作成する。要件が変更された場合でも、承認済バージョンを再検討することで簡単に更新することができる。テキストベースのオープンソース機能ツールであるTextTestは、この承認テストをサポートする。
トレーナでソフトウェア開発者、アーキテクトのEmily Bache氏はEuropean Testing Conference 2017で、TextTestを使用した承認テストのワークショップを行なった。このカンファレンスに関してInfoQは,Q&Aや要約,記事を通じてお伝えしていく。
Bache氏のワークショップは、承認ベースのテストという概念を説明することから始まった。氏はまず、TextTesでテストをセットアップして、最初の実行からアウトプットを得る方法を示した。
ワークショップの参加者は、TextTestを使った承認ベースのテストとして、テストケースの定義と実行を体験した。彼らは最初の実行で生成されたアウトプットをチェックして、プログラムが正しく動作しているかどうか確認した。アウトプットがOKであれば、テストを“パス”とマークして、それを承認した。
アウトプットが正しくなければ、問題が見つかったということだ。実行結果を承認する代わりに、テスタがそれを修正するか、あるいは開発者に向けて実行結果にそれをメモしておいた上で承認することになる、とBache氏は説明した。生成されたアウトプットが承認されたものと異なるため、TextTestはそのテストを“失敗”とマークする。プログラムが修正されてバグが除去されれば、テストの再実行でパスするはずだ。
InfoQは承認テストの技法についてEmily Bache氏と話し、TextTestが承認テストでどのように利用可能なのかを聞いた。
InfoQ: 承認テストとは何でしょう?
Emily Bache: 基本的な考え方は、コードの現在のアウトプットを、これまでのテスト実行で収集した“承認”バージョンのものと比較して、テストがパスしたかどうかを判定する、というものです。
従って、新たなテストを最初に実行した時には、承認に使用する比較対象がありません。それでも、ユーザストーリの会話から求められている結果を書き留めたスケッチやノートなどから、期待される結果はある程度は分かっているでしょうから、コードのアウトプットを手作業で調べて、それが十分であるかどうかを判断します。いくつかの手計算をしたり、確信が持てなければ専門家であるユーザに確認することも必要でしょう。その動作が維持する価値のあるものだと判断すれば、そのアウトプットを“承認”して保存しておきます。以降の実行では、実際の出力結果を承認されたバージョンと比較します。たいていは単純なテキスト差分ツールを使った比較を行なって、何らかの違いがあればテストが失敗したと判定します。
“Minesweeper” Code Kataのようなものが対象の場合は、問題はアスキーアートの入力(地雷(mine)の長方形フィールド)と期待されるアウトプット(同じ長方形フィールドに地雷と地雷の数を加えたもの)として定義されることになります。この例では、各テストケースがインプットの地雷原と承認されたアウトプットソリューションで構成されるので、承認ベースのアプローチに非常によく適合します。テストが失敗した場合には、一般的にはdiffプログラムが、何が間違っているのかを明確に示してくれます。実際に私は、承認テストの練習用にそれを作って、github: Minesweeper Approval Kata で公開しています。
新たな地雷原を設計(これこそ本物のコーナーケース(corner-case)?)して、承認テストに回すことも可能です。同じようなことをYatzyとGilded RoseのKataでも試してみました。アウトプットはこのようなグラフィカルなものではありませんが、同じアプローチが可能です。
InfoQ: アサーション(assertion)ベースのテストとの違いは何ですか?
Bache: “アサーションベース”のテストでは、確認するアウトプットの一面を選択して、そのために特別なコードを書く必要があります。テストには3つの部分 – アレンジ、実行、アサート – がある、という意見をよく耳にしますが、承認テストでは最初の2つはほぼ同じで、変更されるのは第3の部分になります。テスト設計者がテストシナリオとその実行トリガを決めなくてはならないという点は変わらないのですが、アウトプットを事前に、少なくとも詳細に定義しておく必要はありません。テストの最初の2部分がセットアップできれば、即座にそれを実行して、アウトプットを評価することができるのです。アウトプットが不完全であったり、概略であった場合には、アウトプットが維持する価値のあるものになるまでコード開発を続けることになります。処理結果が“承認”に値するものになって、テストをチーム共有することができたとしても、話がそれで終わりとは限りません。テストとは本質的に“アジャイル”なものなので、承認されたバージョンを見直すことが可能ですし、要件が変更されたときにはアップデートすることになるからです。
InfoQ: 承認テストで、どのようにTestTextを使用するのでしょう?
Bache: TextTestはオープンソースのツール(http://texttest.org)で、私やその他の人たちが長年にわたって開発を続けてきました。開発言語に依存せず、個々の関数やクラスよりも実行プログラムのレベルでテストを行なうように設計されています。私自身も、Java、Scala、C++、Python、Rubyで記述されたプログラムのテストに使用しています。
プログラム全体のテストだけでなく、プレーンテキスト出力の比較も実行します。もちろんすべてのプログラムが、通常の機能としてプレーンテキストを出力する訳ではありませんが、大部分はプレーンテキストに変換可能なものを生成します。例えばpdfやhtmlドキュメントはプレーンテキストに変換できますし、複雑なユーザインターフェースをアスキーでレンダリングしたり、データベースのクエリを行なってプレーンテキストのレポートを生成することも可能です。私の経験では、アプリケーションでこのような変換を行なってテストハーネスを生成できるようにしておく価値は十分ありますし、多くのテストケースで再利用されることになると思います。テストが主にプレーンテキストを含むファイルのディレクトリ構造で構成されている場合には、searchやdiff、正規表現やバージョン管理など、手元にあるパワーツールの支援が受けられます。
InfoQ: 承認テストを行なうのはどのタイミングで、どのようなメリットがあるのでしょうか?
Bache: 私はエンド・ツー・エンドのAPIテストに承認テストを使用して、全機能がユーザの期待通りに動作することを確認しています。アウトプットを“承認”するというニーズのためには、それが自然なことだと思うから – ユーザあるいはその分野の専門家が読んで理解できるものをベースとしてテストを行ないたい、という考えからです。pdfドキュメントやWebページ、GUIのスクリーンショットといった、彼らが慣れたツールでテスト結果を示すことができれば、彼らが関心を持っていることがチェックされているという点で、テスト結果をより信頼してくれるはずです。アサーションベースのテストでは、多くの場合、彼らの共感を得るのが非常に難しいのです。
アサーションベースのテストでもうひとつ問題なのは、新たなテストケースごとにアサーションコードを書かなくてはならない点です。承認テストでは多くの場合、以前のテストケースで記述したテストハーネスの再利用が可能なので、維持すべきコード全体の量が少なくて済みます。
私はこのアプローチをレガシコード、つまり、テストカバレッジが不足していて簡単には変更できないようなコードにも頻繁に使用しています。さまざまなシナリオを起動してプログラムが正しく動作していることを承認できれば、つまりは回帰テスト用のスイートが出来上がったということになるからです。
これらはすべて有用な側面ですが、承認テストの真の“キラー機能”は、テスト設計時に予想していなかった不具合を発見できることにあります。アサーションベースのテストでは、チェックするアウトプットのいくつかの側面を明示的に決定しなくてはなりません。左上隅にある正方形が2つの地雷に隣接しているか、請求書に支払額と銀行口座と顧客番号が記載されているか、といった具合にです。ですが、もしも地雷原の最後の列が欠けていたらどうなりますか?あるいは、請求書に送付先ではなくてスタックトレースが含まれていたら?このような場合のアサーションは書いてあったでしょうか?
承認テストはデフォルトでアウトプット全体を比較しますから、私の経験から言えば、このような問題を検出するチャンスが大きくなります。ですが、実際には、チェック対処としたくないアウトプット部分があることも少なくありません。今日の日付やプロセスID、あるいはランダムな数値などです。こういった値は毎回違うので、承認バージョンと比較する前に除外しておく必要があります。つまりは、テスト設計者の役割が違うということです – アサートする対処をいくつか選び出すのではなく、無視するものを選択するのです。こういった思いがけない変化をキャッチしておくことが必要で、そうでなければ1日を台無しにしてしまうことになります。
InfoQ: 承認テストについて詳しく知りたいテスタは、どうすればよいのでしょうか?
Bache: そうですね、まず言いたいのは、これはテスタのためだけの技術ではない、といことです。すべての開発者がコードのテストから恩恵を受けると思いますし、新機能の開発をテスト駆動で行なう方法としてこの技術を利用できます。私の経験から言うと、このようなテストスイートは、開発者とテスタ、そして専門家であるユーザないしプロダクトオーナが共同作業することによって、より多くのメリットを引き出すことが可能になります。
新しい技術を学ぶためのよい方法は、まず自分がすでに精通した問題で試してみることです。MinesweeperやYatzy、あるいはGlided RoseなどのCode Kataを行なった経験があるならば、私のgithubページにあるそれらの承認テスト版を見てみることをお勧めします(Yatzy Approval Kata, GildedRose Approval Kata)。一度試してみれば、あなたのそれぞれのテストと私の“サンプルソリューション”版を比較できます。この手の題材を今後も追加したいと思っています – 好みのCode Kataをこの方法で解決してみたいのであれば、その旨をご一報ください。
アプローチのバックグラウンドや具体的な事例、理論について知りたいのならば、いくつかの記事や、科学論文も公開されています。例えば私は、自分の(現在執筆中の)書籍“Mocks, Fakes and Stubs”で承認テストに関する章を書いていますが、その内容はダウンロード可能な無償サンプルに含まれています。学術的には、私が参加したプロジェクトの研究成果が“On the Industorial Applicability of TextTest”という論文で発表されました。私のブログにも承認テストに関する記事があって、他の人たちが同種のテストアプローチをどのように利用しているか、という内容を説明しています。中でもLlewellyn Falco氏はスクリーンキャストやビデオなど、このトピックに関するたくさんの資料を作成しています。“Using ApprovalTests in .Net”はお勧めです。TextTestのWebサイトにも、多数の記事やユーザ向け資料があります。
この記事を評価
- 編集者評
- 編集長アクション