事例で学ぶテストピラミッドを使ったテスト戦略

2018年3月8日(木)
松尾 和昭

はじめに

本連載は、開発を加速・効率化させるソフトウェアテストをテーマに解説を進めています。前回までの解説で、読者の方々はソフトウェアテストという言葉からその役割やアプローチ、その種類や区分といったことを想像できるようになったのではないでしょうか。

今回は、実際の開発に適用することを想定し、テストピラミッドを中心に機能に対するテストに対してどのような戦略を採ることができるかを解説します。その上で、実際の計画では何をどのように適用して開発を進めるかということをチームで議論・決められるようになることを期待しています。

なお、ここではAndroidアプリの開発を題材にします。そのため、時折Androidに関する言葉が登場します。これはGoogleが公式にAndroidプラットフォームにおけるテストの基礎を提示しており、説明し易いためです。この方法はAndroid以外にも適用が可能です。

ピラミッドとサイズによるテストの大枠を整理する

Google I/O 2017における講演「Test-Driven Development on Android with the Android Testing Support Library」での発表以来、GoogleはAndorid開発者向けWebサイトの「 Fundamentals of Testing」というページでAndroidアプリの開発フローと、それに対するテストについて解説しています。その中の「Understand the Testing Pyramid」 という段落でテストピラミッドに触れており、ピラミッドを大きくUnit Tests/Integration Tests/UI Testsの3つに区分しています(図1)。

図1:The Testing Pyramid
「Fundamentals of Testing」の「Understand the Testing Pyramid」より転用

このテストピラミッドは第3回の「テストレベル」の項でも軽く触れましたが、ピラミッドの下位から上位に向かうにしたがってコストがかさむことを表しています。幅がテストケースの実行量を表現し、より下位のテストにテストケースを移動した方がコストの改善に繋がることを意味します。 第1回の「開発を加速させるテスト」の項で解説したテストを充実させるためには、テストをこのピラミッドの下位へ寄せていくように工夫する必要があります。

ここでいう「コスト」とは、実行時間やテスト対象となる要素の範囲を指します。AndroidアプリのUIを操作して確認する類のテストと、メソッド単位で確認するテストを想像してみると分かり易いでしょう。メソッド単位の方がより高速に限られた範囲をテストできるため、コストが低くなると表現しています。

また、「Fundamentals of Testing」では「テストサイズ」という表現も併用しています。この言葉は「Google Testing Blog」の「Test Sizes」から目にするようになりました。テストの実行をSmall/Medium/Largeに区分することで、各々の目的と実行環境を区別することを目指しています。

Android開発では、これらの区分を「Testing Support Library」でJUnit4におけるannotationとして提供しています。日本語の資料では「Android/iOSアプリのテストの区分戦略」 などで具体的な区分例を見ることができます。

例えば、Android開発ではSmall/Medium/Largeを次のように区分することが可能です。

Androidアプリ開発ではいくつかのテスト実行環境が存在します。JavaVM環境上で実行可能なテストケースから、Android環境上でなければ実行できないものまであります。そのため、実行環境に依存した形でテストサイズを区分できます。

テストサイズを開発に適用する

第2回の「テストとして何をするかを決めよう」の項で紹介した継続的デリバリーのモデルを参考に、Androidアプリ開発を考えてみます。継続的デリバリーのテスト戦略ではプログラム修正の度にコンパイルや静的解析、単体テストなどを自動化しておき、最後にワンクリックで本番環境へデプロイできると解説がありました。今回のAndroidアプリ開発では本番環境にデプロイされるまでではなく、リリース可能なバイナリファイルが生成されるまでを考えます。

すでにCIサーバが存在することを前提に、まずは次の手順を想定します。CIサーバについても第2回の「オススメは静的解析ツール/単体テストの自動化/CI」で少し触れていますが、ここでは特に具体的なサービス名までは限定せず、何らかのプログラムの変更に対して自動テストなどが実行される環境と想定してください。

  1. 開発用ソースコードのプログラムを修正・保存し、自動テストを実装する
  2. CIサーバが修正に対して自動テストを実行する
  3. (結果がすべて問題なければ)その修正をリリース用ソースコードに適用する
  4. リリース用ソースコードからリリース可能なバイナリファイルを作成する
  5. リリース可能なバイナリファイルが作成される

この手順の流れに対して、前述したテストサイズの実行タイミングを考えます。例えば、次の流れで適用するとします。

  1. 開発用ソースコードのプログラムを修正・保存し、自動テストを実装する
  2. CIサーバが修正に対して自動テストを実行する:Small Tests /Medium Testsを実行
  3. (結果がすべて問題なければ)その修正をリリース用ソースコードに適用する:Large Testsを実行
  4. リリース用ソースコードからリリース可能なバイナリファイルを作成する
  5. Google Play StoreへAPKをアップロードする

この手順の流れを図に照らし合わせると図2のようになります。1~5の手順に対して、緑色の丸で各々のテストを実行するような形です。

図2:Small/Medium/Large Testsの実行タイミング

この手順の流れでは、開発者が修正を加える度に実行速度の速いSmall TestsとMedium Testsを実行します。ただし、その後のLarge Testsはリリース用ソースコードに対して定期的に実行するにとどめています。これは、画面描画を含むテストケースの方が実行時間や実行環境に制限があるため、Large Testsだけ異なるタイミングで実行するようにしたという例です。

今回のテストピラミッドやテストサイズでは手動テストについて言及していませんが、これらが手動テストを全く考慮していない訳ではありません。テストピラミッドのUI Testsの中に手動テストを織り交ぜて定義しても良いでしょう。その場合は手動テストの実行順序の調整が必要にもなるでしょう。また当面はLarge Testsのテスト自動化は行わず、手動テストを中心にするという計画も採ることが可能でしょう。

なお、今回はテストサイズを自動化されたテストに限定しましたが、テストピラミッドでは自動化されたテストに限定していません。その辺りは読者の環境に合わせて調整して行けば問題がない範囲です。大事なことは「どこに注力し」「何に注力しない」かの境目を作り、目的に向けて必要なものを適用できるようにすることです。

ところで、実際にこのような環境を用意していざSmall Testsやより小さなサイズのテストを増やしていこうと考えると、テスタビリティを考慮したアプリのアーキテクチャを考慮していく必要がでてきます。昨今、モバイルアプリの世界ではMVPやMVVM、VIPERなど様々なアーキテクチャの話題を目にします。これらを適用するとテストピラミッド、テストサイズ共により小さな領域で様々なロジックを確認できるようになります。そうすれば、テスト実行の自動化も比較的容易になってきます。

またテスト自動化を推進する場合では、アーキテクチャレベルでのテスタビリティを作りこんだ上で自動化した結合テストの責務を増やし、テストの保守性を高める工夫がしばしば採用されます。

引用:第3回「段取りを構成するテスト活動の部品」節「テストレベル」項より

テストサイズを使ってやる/やらないを分ける

先ほど紹介した「Test Sizes」を参考にすると、Small Testsでは1つもしくは数個のメソッドのテストに注力しており、それ以外は仮の環境に置き換えても構わないとしています。そのため、この段階では複数のメソッドを連ねて1つの大きな機能を実現するところや画面が正しく描画されるかまでは考慮しません。

一方で、Large Testsはより本番環境に近い状態でテストを実行しようとしています。そうなると、テスト実行環境の構築が困難になったり、非効率になったりする場面も出てくるでしょう。例として、画面の種類は少ないが入力に対する処理が複雑なメソッドをテストするケースを考えてみます。本来はJavaVM上でさまざまなバリエーションに対して可能な形に落とし込めるのに、それらのテストをすべて画面ベースのLarge Testsで実装したとします。そうすると通常の何倍もの実行時間がかかってしまいます。

このように、テストサイズはどの区分で何をやるか、やらないかの足がかりとなります。テストピラミッドではそれらの量をどうしていきたいか、現状はどうなのかを分析する手助けをします。

開発の途中から適用する

これらの環境は確かに何らかの開発の初期から適用できることが望ましいでしょう。とは言え、すでに進行中の開発物に対して適用する必要が出てくる場合も多いはずです。その場合でも、まずはチームでテストピラミッドやテストサイズなどを定めて、現状を把握することは重要です。そこから改善できる箇所を見つけ、より健全な状態に持っていくきっかけを作ることが大事だからです。

現状を知り、改善箇所を見つけた後はそれらの改善を進める段階になります。まずはCI環境を作るところからになる可能性もあります。MVPを導入して開発サイクルを大きく崩すことなく書き換えを進めることが必要になることもあるでしょう。

何においても、開発途中だとしても、このような大枠を形作り自分たちの環境に適用することは重要です。

現在、著者は英語を主言語とした環境で活動しています。日本語ほど流暢に互いの意思疎通を測れない環境においても、ピラミッドなどの区分は現状を把握し、これからどう進むかという議論を支える道具となりました。そのため、今回のような大枠に対する話は開発が進む中でも適用可能な話だと信じています。

おわりに

今回は、前回までに解説されてきた開発を加速・効率化させるソフトウェアテストのうち、開発の中でどのようにテストを区分し、プロセスに組み込むかについて解説しました。そのため、話題をテストピラミッドやテストサイズに話題を絞りました。また、特にAndroidアプリ開発を対象にしましたが、技術的にできる/できないなどの難易度の差はありつつも、流用の利く考え方だったのではないでしょうか。

次回は、製品開発のためのテストという視点で深掘りして解説します。

テスト自動化研究会 / クックパッド株式会社
現在、日本国外を対象としたサービスの品質・テスト関連の活動に主に関わる。複雑系が技術的な興味の対象。OSS活動の代表的なものとして、Appiumプロジェクトのテクニカルコミッターとしても活動している。日本国内では、いくつかのイベントにおいて講演などを行う。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています