2024年2月28日
テストピラミッド万歳
(2023-9-6)by Ramona Schwering
本記事は、原著者の許諾のもとに翻訳・掲載しております。
クイックサマリー:「テストピラミッド」は、自動テストをUI、サービス、ユニット単位に整理することで、開発に自動テストを組み込む方法を示すために作成されました。2012年に定義されて以降、このモデルは次第に使われなくなってきたように思いますが、本当に廃れてしまったのでしょうか。この記事では、最新のテスト戦略を紹介するとともに、今日のソフトウェア開発におけるテストピラミッドの関連性を検討します。
筆者の同僚であるジャン・フィリップ・ピエトルチェクが、かつてコードを書く開発者の責任について、次のように述べました。
none
「我々の仕事の成果を最終的に使用する人々は、(中略)我々がただ最善を尽くすだけでなく、実際に機能するものを作ることを期待しているのです。」
— ジャン・フィリップ・ピエトルチェク
彼の言葉は、私たちが書くコードをそれに依存する人々の観点からとらえている点で非常に印象に残りました。目まぐるしく変化するこの世界では、ユーザーは私たちが可能な限り最高のコードを記述し、開発したソフトウェアが実際に機能することを期待しています。このレベルの期待に応えるのは当然容易ではなく、だからこそ、開発スタックにおいてはテストが重要な要素になるのです。テストでは、仕事の質を評価し、さまざまなシナリオに照らして検証を行うことで、顕在化する前に問題を特定します。
テストピラミッドは数あるテスト戦略の内の一つです。2012年に紹介されて以降、おそらく10年近くは主要なテストモデルとして利用されてきましたが、最近は以前ほど参照されなくなっています。テストピラミッドは今でも定番のテスト手法なのでしょうか。それとも、他の手法も多数台頭してきている中、昨今の開発事情により適した最新のモデルの影に隠れ、埋もれてしまっているのでしょうか。
この記事ではその点を探っていきたいと思います。
テスト戦略の目的 #
ユーザーの信頼を得るためには、書いたコードによって製品がユーザーの期待どおり確実に機能するよう、強固なテスト戦略が求められます。優れたテストコードを書くためには、まず何から始めればよいのでしょうか。テストコードはいくつ必要でしょうか。これまで多くの人がこれらの問いへの答えを見つけようと取り組んできました。しかし、求めていた気づきを私に与えてくれたのは、ケント・C・ドッズの次の一言でした。
none
「最大の問題は、何をテストするのかということと、実装に関する詳細をテストすることで得られる偽りの自信ではなく、真の自信が得られるような方法でテストを行うにはどうすればよいかを見極めることです。」
— ケント・C・ドッズ
これが出発点です。テストの目標を決めることが、テスト戦略を策定する上で最も重要な作業になります。ネット上には誤った判断を揶揄したミームが溢れていますが、そうした判断の多くは、単にテストの目的や、いくつテストを行えば確信が得られるのかが不明であったことに起因します。テストには、コードを適切に検証し、コードが期待どおり機能することを確認するための「最適な比率」があります。
2 unit tests. 0 integration tests. pic.twitter.com/K2MZKwr8JT
— DEV Community (@ThePracticalDev) August 2, 2017
2つの単体テスト統合テストなし
問題は、さまざまなユニットを結合してどう連動させるのかについての戦略を持たず、1種類のテスト(多くの場合は単体テスト)にのみ重点を置く開発者が多いことです。例えば、洗面台のテストを行う際、蛇口と排水口を別々にテストしても、両者が正常に連動するのかは分かりません。単体テストでは蛇口が問題なく機能していても、排水口が詰まっているにも関わらず蛇口から水が流れ続ければ、洗面台として正常に機能しているとは言えません。
テスト手法について説明する際、ピラミッドモデルの例のようにしばしば「形」が用いられます。この記事では、筆者が見てきた形をいくつか紹介し、それぞれの形を実際のテストに当てはめるとどうなのか、結論としてどのテスト戦略が、筆者の考える今日の開発に適した包括的テストの基準を満たすのかを考察したいと思います。
基本に戻る #
その前に、各種テストの一般的な定義をおさらいしましょう。
手動テスト
- 人の手によって行われるテストです。実際のユーザーがユースケースに従ってアプリ内をクリックして回り、場合によってはユースケースに記述されていない方法で予期しないシナリオでのアプリの動作を検証します。多くの場合、このテストは製品チームが観察する中、ユーザーとのリアルタイムでの対面もしくはリモートインタビューの形式で行われます。
単体テスト
- このタイプのテストでは、アプリを「ユニット」と呼ばれるテスト可能で独立した小さな構成要素に分け、各ユニットを個別にテストし、正常に動作することを確認します。
統合テスト
- コンポーネントまたはシステム間の連携に重点を置いたテストです。各ユニットを組み合わせてテストし、統合された状態で全体として正常に機能することを確認します。
E2E(エンドツーエンド)テスト
- このタイプのテストでは、コンピューターが実際のユーザーとのやり取りをシミュレーションします。一定の手順に従うことが求められる作業をユーザーが完遂できるのか、期待する結果が得られるのかなど、ユーザーの視点に立って検証を行うためのテストと考えてください。インプットに対して適切なアウトプットが得られるのか確認することで、ユーザー体験全体を検証します。
では、これらのテストをどう組み合わせればよいでしょうか。従来アプリケーションを問わず定番として用いられてきたのが、各テストを包括的なテストスイートとしてまとめたテストピラミッドです。
偉大なるテストピラミッド万歳 #
マイク・コーンが著書『Succeeding with Agile』で最初に紹介し、マーチン・ファウラーが「実用的なテストピラミッド」という記事でさらに発展させたテストピラミッドは、パフォーマンスとコストに基づいてテストを優先順位付けしています。異なる粒度のテストを記述し、高位レベルのテストは少なく、素早く安価で実施できる信頼性の高い単体テストは多くするよう推奨しています。テストの順序としては、素早く低コストで実施できるものから、時間とコストのかかるものの順に行うよう推奨しています。ピラミッドの最下部には多くの単体テストが位置し、次にサービス(統合テスト)が真ん中に来ます。そして最上部に、E2Eテストを含むより具体的で少数のUIテストが位置します。
3層に分かれたピラミッドの手書きイラスト(拡大プレビュー)
関係者の間では、テストピラミッドはテスト構造のあるべき姿を単純化しすぎているとの声が高まっています。約10年前にテストピラミッドについての記事を執筆したマーチン・ファウラーは、この問題について最近ブログ記事を書いています。筆者のチームでは、このモデルは私たちの仕事をエンドユーザーに近づけるものなのか、それとも遠ざけるものなのかという問いも出ています。ピラミッドの高位層は単体テストの信頼性を高め、より優れた価値をもたらしますが、各構成要素がどう連動するのかという大局的な視点に欠けるように思われます。少なくとも私たちには、テストピラミッドが時代遅れになりつつあるように感じられました。
ピラミッドからダイヤモンドへ #
筆者のチーム内で議論された論点の1つが、ピラミッドが単体テストに重点を置きすぎているということです。ピラミッドは、単体テストの性質と対象範囲を説明する形としては非常に優れています。しかし、単体テストとは何かと4人に問えば、おそらく4通りの回答が返ってくるでしょう。明確化のため、ピラミッドの形を少し変える必要があるかもしれません。
筆者のチームが最も明確化する必要があったのは、単体テストを「いつ」「どこで」で止めるかということです。ピラミッドの形は、単体テストがテストプロセスの大部分を占めることを示唆しますが、私たちはそれが間違っているように感じました。結局のところ、単体テストをまとめるのは統合テストなので。
したがって、テスト戦略を表すピラミッドの見方を変えてみると、ダイヤモンドの形に変化します。
4層に分かれたピラミッドの手書きイラスト(拡大プレビュー)
統合テストは、単体テストには複雑すぎる場合があるため、テストピラミッドの「忘れられた層」と呼ばれることがあります。しかし、テストダイヤモンド(しばしば2つの特定の層に分けられる)ではより重視されます。
-
統合テスト層 この層は、テストピラミッドの場合とほぼ同じですが、「単体テストとして行うには規模が大きすぎる」と見なされるテストが割り当てられます。つまり、単体テストの層と統合テストの層の中間に位置します。この層には、特定のコンポーネントなどのテストが適しています。
-
システム統合テスト層 この層は、APIから取得したデータなどの「実際の」統合テストを中心に扱います。
したがって、ダイヤモンドの形は統合テストを行った直後に単体テストを行うプロセスを示唆しますが、それらの単体テストはピラミッドモデルの場合ほど重視されません。そのため、単体テストが重視されなくなった分、統合テスト層に相応の予算が割り当てられます。
手動テストの位置付けは? #
テスト戦略が「ピラミッド型」であれ「ダイヤモンド型」であれ、重要な手動テストが依然としてプロセスに欠けています。自動テストは確かに重要ですが、手動テストが不要になるほどではありません。
自動テストと手動テストは互いに欠かせないものだと思います。自動テストは定型的な共通作業を不要にし、人間がより注力すべき部分にテスターが集中できるようにする必要があります。自動化は手動テストを置き換えるのではなく、補完すべきなのです。
この点を加味した場合、ダイヤモンドやピラミッドの形はどうなるでしょうか。手動テストはどの層にも含まれていませんが、どこかに入れるべきです。自動テストは効率的にバグを検出してくれますが、より包括的なテストを行うためには手動テストが必要です。とは言え、主に自動テストに重点を置いたテスト戦略が理想的であるのも事実です。
これをテスト戦略の形に当てはめると、ピラミッドやダイヤモンドというよりアイスクリームコーンのような形になります。
4層に分かれた、アイスクリームコーンに似た逆ピラミッドの手書きイラスト(拡大プレビュー)
これは実際に「アイスクリームコーン」と呼ばれる手法です。実装までの時間は長くかかりますが、より確かな自信が得られ、より多くのバグを検出できます。2015年に発表された記事で、サイード・ガトソンが簡潔に説明しています。
しかし、ピザのような形で実際にテストの内容を説明しきれるのでしょうか。この概念はグレブ・バームトフによって突き詰められ、彼が「Testing crab(蟹)」モデルと呼ぶ形に集約されました。この手法では、スクリーンショットを比較し、人間が差異を検証します。目視テストと機能テストは蟹の胴体部分を構成し、その他のテストは蟹の手足を構成します。テストの際に実装前と実装後のスナップショットを撮影できるツールがあり、撮影したスナップショットを重ね合わせることで、視覚的なリグレッションを確認できます。
テストトロフィー #
どのテスト手法もコストがかさみますが、テストピラミッドはその点を適切に表しています。ただ、その形自体が現実的ではないか、テストの内容全体と、各テスト層に置かれた重点を考慮するのに効果的ではない可能性があります。したがって、各テスト層と、それぞれどの程度重視すべきかを正確に表したこれらすべての手法の間に妥協点を見つける必要があります。
ギレルモ・ローチが2016年にこの点を簡潔に表現した次の言葉を筆者は気に入っています。
Write tests. Not too many. Mostly integration.
— Guillermo Rauch (@rauchg) December 10, 2016
テストを書こう。ただし書きすぎないこと。主に統合テスト。
少し掘り下げてみましょう。
- テストを書こう 信頼を築くためだけではなく、メンテナンス時間を節約できるため。
- 書きすぎないこと 100%の網羅率は、聞こえは良いですが必ずしも得策ではありません。アプリの詳細をすべて漏れなくテストした場合、少なくとも一部のテストはエンドユーザー体験にとって重要ではなく、テストを実行することが目的化しており、維持コストが増えることを意味します。
- 主に統合テスト 統合テストに重点が置かれています。合理的な実行時間を維持しつつ、高いレベルの確信が得られるため、最も事業価値のあるテストです。
ケント・C・ドッズの記事を読んでいる人ならば、以下の概念をご存知かもしれません。彼が提唱する「テストトロフィー」手法では、統合テストの優先度が従来のテストピラミッドよりも高く設定されており、ギレルモ・ローチの主張とも完全に一致しています。
4つの層に分かれた薄い金色のトロフィーの手書きイラスト(拡大プレビュー)
ケントは、包括的なテストが製品の成功において果たす重要な役割について説明しています。彼は、単体テストよりも、製品のコア機能と評価される挙動について理解を深められる統合テストの価値を重視しています。また、彼はモックアップテストの数を減らし、統合テストの数を増やすことを提案しています。テストトロフィーは、テストの粒度を従来とは少し異なる方法で表した比喩であり、テストを以下の種類に分類しています。
-
静的分析:これらのテストはデバッグの手順を実行することで、素早くタイプミスを特定します。
-
単体テスト:テストトロフィーでは、テストピラミッドほど重視されません。
-
統合テスト:テストトロフィーで最も重視されるテストです。
-
ユーザーインタフェース(UI):E2Eテストや目視テストを含み、テストピラミッドと同様にテストトロフィーでも重要な役割を担います。
「テストトロフィー」はユーザーの視点を重視しており、費用対効果に優れています。では、テストトロフィーが最も推奨される手法なのでしょうか。テスト戦略としては最も理にかなっていますが、問題もあります。単体テストには依然として有益な利点がありますが、統合テストやE2Eテストには、ランタイムが長いことや信頼性が低いことなどの欠点があります。単体テストの利点は妥当であり、筆者は今でも好んで使用しています。
テストピラミッドは古いのか?#
テストピラミッドは、ソフトウェア開発においてアプリケーションが正常に機能することを確認するのに役立つテストモデルとして、今でも人気があります。しかし、どんなモデルでもそうであるように、欠点もあります。最大の問題は、単体テストの定義を明確化しなくてはならないことです。
筆者のチームは、テストパイプラインでダイヤモンドの形に改良を加えたものを使用しました。その結果、完全に間違ってはおらず、単に不完全なだけであることが判明しました。特に各種テストの優先順位を決める上で、今でも貴重な知見を得ています。
ジャスティン・サールズがうまく言い表しているように、開発チームが教科書通りのテストパターンに従うことはほとんどありません。
People love debating what percentage of which type of tests to write, but it's a distraction. Nearly zero teams write expressive tests that establish clear boundaries, run quickly & reliably, and only fail for useful reasons. Focus on that instead.https://t.co/xLceALKrWe
— Justin Searls (@searls) May 15, 2021
どのタイプのテストを何割書くかということについて盛んに議論されていますが、本質からずれていると思います。境界を明確に定め、素早く確実に実行され、失敗する際には必ず有益な理由がある表現豊かなテストを書いているチームはほとんどありません。重視すべきはそちらでしょう。
@MartinFowlerのテストピラミッドはもう古い。 統合テスト>単体テストが新常識でしょう
フロントエンドでは、@rauchgと@kentcdoddsが提唱する「テストトロフィー」があります
バックエンドでは、@theburningmonkのコースで@SpotifyEngの「テストハニカム」が提唱されています
これは、筆者のチームの経験上でも言えることで、テストを切り分けたり定義したりするのは難しい場合が多いです。それは別に悪いことではありません。マーチン・ファウラーでさえ、異なるテストモデルがテストの対象範囲に対する見方にプラスの影響を与えていることを強調しています。
したがって、テストピラミッドが古いとは決して考えません。むしろ、今も昔と変わらず知っておくべきものだと思います。しかし、ポイントはテストモデルの「形」にこだわりすぎないことです。覚えておくべき最も重要なことは、テストは素早く確実に実行されるべきであり、実際に問題があるとき以外は失敗してはいけないということです。単に網羅的なテストを目指すのではなく、ユーザーの役に立つことが重要です。最も重要な作業は、テストの設計段階でこれらの要素の優先順位を決めた際にすでに終えているのです。
参考文献 #
- “The Practical Test Pyramid,” Ham Vocke
- “On the Diverse And Fantastical Shapes of Testing,” Martin Fowler
- “The Testing Pyramid Should Look More Like A Crab,” Gleb Bahmutov
- “The Software Testing Ice Cream Cone,” Saeed Gatson
- “Write tests. Not too many. Mostly integration,” Kent C. Dodds
- “The Testing Trophy and Testing Classifications,” Kent C. Dodds
- “Static vs Unit vs Integration vs E2E Testing for Frontend Apps,” Kent C. Dodds
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa