サバンナ便り ~ソフトウェア開発の荒野を生き抜く~

第5回テストピラミッド ~自動テストの信頼性を中長期的に保つ最適なバランス~

粒度の異なる各自動テストをどの程度書くか悩まれている方は多いと思います。今回は、自動テストの理想的なバランスを示す「テストピラミッド」について説明します。

テストピラミッドとは何か

テストピラミッドとは、コスト(記述コストと実行コスト)と忠実性(本物の挙動を反映している度合い)が高く、実行速度と決定性(テストが毎回同じように安定して動く度合い)が低いテストほどケース数を減らすべきだという、自動テストケース数の望ましい比率をピラミッド型に視覚化したものです。

図1は、テストの粒度をユニットテスト、インテグレーションテスト、E2Eend to endテストの三段階で示しており、テストピラミッドの説明によく用いられます。ユニットテストが最も多く、E2Eテストが最も少ない状態に近づけることで、開発速度と信頼性の高いバランスが得られると言われています。

図1 テストピラミッド
図1

なぜ比率を意識するのか

望ましくない比率の例としては、図2のアイスクリームコーン型や、上下に比べ中央がくびれた砂時計型が挙げられます。どちらも上段のテストが多いのが特徴です。なぜそうなるのでしょうか。

図2 アイスクリームコーン型
図2

E2Eテストのように、テスト内容がユーザー視点に近く、検証するコード範囲も最も広くなるテストを書けるようになると、これらのテストだけですべての範囲を網羅できると錯覚しがちになります。しかし、これらのテストは決定性が低いため、コードに変更がないのに結果が毎回変わる、コードが正しいのに失敗するなどの不安定な動作が増えます。そういった信頼不能テストflaky testは失敗時の原因究明を困難にし、開発者はテスト結果を次第に信頼しなくなります。

では上段のテストはすべて不要かと言うとそうではなく、下段のテストだけでもうまくはいきません。たとえばユニットテストだけが書かれている、言わばピラミッドの土台だけといった状況においては、開発したプログラムどうしや外部サービスとの連携などが自動テストの範囲に入っていません。ユニットテストがすべて成功しても、デプロイした本番環境ではシステムが望む動作をしない状況に陥りがちです。結果として、開発者は自動テストの結果だけではシステムの動作に確信を持てなくなります。

つまり、自動テストの粒度において大は小を兼ねませんし、小も大を兼ねません。すべてのテストを書いていく必要があるからこそ、望ましいバランスを考える必要があるのです。

望ましい自動テストの性質

Puppet社が開始し、現在はGoogle社が続けている継続的デリバリの研究には、組織に競争上の優位性をもたらすと認められた自動テストの性質が挙げられています。そのうち、特に次の2つがテストピラミッドの有効性を裏付けています。

  1. テストの大半を統合環境(本番環境と同等のテスト環境)を必要とせずに実施できること
  2. 信頼性の高い自動テストを備えること

ⓐは、ピラミッドの最上段が統合環境の必要なテスト、最上段以外が統合環境不要のテストと整理すると、ピラミッド状に最上段を小さくする重要性につながります。ⓑは、下段を厚くする重要性につながります。信頼性が高いとは、自動テストの成功や失敗が信じられるということです。自動テストが成功すればデプロイ可能、失敗すればコードのどこに問題があるかすぐにわかると確信できる状態です。信頼が失われると、自動テストの記述やメンテナンスに労力が払われなくなります。偽陽性(誤検知)や偽陰性(見逃し)が多いと、じわじわと信頼が失われます[1]。また、テストの実行速度が遅いと実行頻度が下がり、こちらも信頼低下につながります。これらの観点では、上段よりは下段のほうが信頼性の高いテストです。

ピラミッド型の作り方

統合環境なしでその大半を実行し、信頼性が高い自動テスト群を導くピラミッド構造を作るコツは、ユニットテストやE2Eテストのようなテスト範囲ではなく、テストサイズ[2]で段を分割することです。

テストピラミッドは議論が白熱しがちで、⁠ピラミッドはもう古い」⁠トロフィー型、蜂の巣型、⁠ほかにもいろいろ)が良い」などの意見もあります。しかし、それらの議論の多くは「ユニット」「インテグレーション」の解釈の違いから発生していて、あいまいさの少ない一貫した基準で再整理すると、どれもおおむねピラミッド型に近付きます。その基準こそテストサイズです。

ピラミッド型を作るためには、まずテストの信頼性を最も左右する要素、信頼不能テストに注目します。信頼不能テストに深く関係するのが、GUIGraphical User Interfaceを経由した統合環境の操作や、テスト対象と外部システムとの通信です。これらはテストサイズの観点では、テスト実行マシンから外部への通信を伴うLargeテストです。それをピラミッドの上段に閉じ込めます。

ピラミッドの中段にはMediumテストを配置します。Mediumテストは1つのマシンに閉じたテストですが、コンテナ技術などを使えばデータベースやGUIを伴うテストも実行できます。そのため若干の非決定性があり、テストの速度もそれほど速くなく、テストの並列実行にも制約が発生します。

ピラミッドの最下段にはSmallテストを配置します。Smallテストはそれぞれのテスト実行が単一プロセス内で完結しており、非決定性が入り込む余地はほとんどなく、高速に並列動作し、テスト失敗時の原因も箇所も明白です。

テストピラミッドが最も大事にしているのは、自動テストの信頼性を中長期的に保つことです。テストの数が少ないうちはE2EテストやLargeテスト中心でも信頼性を保てるかもしれませんが、テストの数が増すごとに非決定性やテスト実行速度の遅さが信頼性を蝕み、自動テストが破綻していきます。可能な限り早いうちに、テストを下方の段に移植してピラミッド型に近付ける必要があります。

上段のテストを中段に移植するにはテストダブル[3]を使います。中段のテストを下段に移植するのにもテストダブルが使えますが、オンメモリでテスト対象ロジックを完結させてテストダブルの必要性を減らすような設計変更、たとえばドメイン層の抽出なども非常に効果的です。

ピラミッドの各段の構成比は、数値目標を追い求めて本来の目的を見失わないように注意してください。そのうえで、指標としては「下段:中段:上段=70:20:10」を目安とし、システムが大規模になるにつれて自動テストの決定性や速度がさらに重要になりますので、80:15:5を目指すのがよいでしょう。

おわりに

自動テストの信頼性を中長期的に保つというテストピラミッドの本質を理解しましょう。

おすすめ記事

記事・ニュース一覧