「テスタビリティ」を作り込む

2018年6月14日(木)
井芹 洋輝

はじめに

今回は、ソフトウェアテストに関わる個別テーマとして「テスタビリティ」(テスト容易性、試験性)について解説します。

テスタビリティとは、「どれだけ容易にテストできるか」「どれだけテストを実現できるか」の度合いを示すものです。テストにコストをかけているならば、テスタビリティの高低はチームの生産性に影響します。今回はこのテスタビリティを対象に、具体像や作り込みのアプローチを解説します。

テスタビリティの概要

テスタビリティには、例えば「テスト対象の網羅のしやすさ」「テスト自動化のしやすさ」「テストに影響のある変更の少なさ」「テストが求めるテスト環境の複雑さ」が含まれます。テスタビリティを向上させると、次のメリットが得られます。

テストの効率性の向上。より小さなコストやリソースでテストできるようにする
テストの有効性の向上。もともと実現が困難だったテストの実行や、既存のテストの用途拡大を促進する

これらのメリットはチームの生産性を左右するため、テスタビリティの向上は多くのプロジェクトで重要な課題となります。

テスタビリティの具体像

テスタビリティの具体像として、いくつか学習に有用な定義や標準があるので紹介します。

まず、James Bach(『ソフトウェアテスト293の法則』著者、日経BP社刊)はテスタビリティの具体的な特性として、次の要素を挙げています(訳は(ロジャー・S・プレスマン著『実践ソフトウェアエンジニアリング』(日科技連出版社刊)から引用)。これはテスタビリティの詳細例として参考にできます。

  • 実行円滑性(Operability)
    テスト実行での支障の少なさ。例:テスト実行を妨げるバグの少なさ
  • 観測容易性(Observability)
    テスト対象の出力の取得しやすさ。例:エラー検出のしやすさ・内部状態の取得手段の有無
  • 制御容易性(Controllability)
    テスト対象の制御のしやすさ。例:内部状態の操作手段の有無
  • 分解容易性(Decomposability)
    テスト対象の分離のしやすさ。例:テストダブルへの置換のしやすさ
  • 単純性(Simplicity)
    テスト対象のシンプルさ。例:冗長な機能の少なさ
  • 安定性(Stability)
    テストに影響ある変更の少なさ。例:インターフェースの変更の少なさ
  • 理解容易性(Understandability)
    テスト対象の理解しやすさ。例:API仕様の可読性

次に、ISO/IEC 25010(System and software quality models。JIS版はJIS X 25010)では、品質モデルとしてテスタビリティ(規格中は試験性と呼称)の位置づけや定義を記述しています。また関連規格のISO/IEC 2502nシリーズでテスタビリティの具体的なメトリクスを提示しています。

そこでは、製品品質を「機能適合性」「性能効率性」「互換性」「使用性」「信頼性」「セキュリティ」「保守性」「移植性」の8つの特性に分類した上で、保守性に属する副特性としてテスタビリティを体系づけています。

これらISO/IEC 25010やその関連規格は、テスタビリティの位置付けの例として参考にできます。

こうした分類や標準は、記述どおりに実践すれば成果が出せるというものではありませんが、テスタビリティを考える上でイメージの具体化、分析の整理、知識の抜け漏れのチェックなどを行う一助になります。

テスタビリティの実装

テスタビリティの実装は、大よそ「コードレベルのテスタビリティ」と「システムレベルのテスタビリティ」に大別されます。前者はコードのメソッドや内部状態などを扱うもので、後者はシステムとして統合された状態で利用できるものです。

主なテスタビリティの実装として、以下のものがあります。

  • 制御点(Control Point)
    テスト対象を操作するための実装。制御容易性を支える。コードレベルではAPI入力、テストスタブ、デバッガによる状態セット手段などがある
  • 観測点(Observation point)
    テスト対象の出力を得るための実装。観測容易性を支える。コードレベルではAPI出力、テストスパイ、例外などがある
  • 接合部(Object Seam)
    テスト実行時にコンポーネントを切り替えるための実装。用途は多様で、テスト対象の切り出しや支障のあるコンポーネントの置換を可能にする効果で分解容易性を支えるほか、バグの多いコンポーネントの隔離といった形で実行円滑性や安定性を支える。また、重要な役割として制御点・観測点などのテスタビリティの仕組みを後から注入可能にする手段としても活用される。コードレベルではDependency Injection、リンク切り替えなどがある。

上記以外にもさまざまなものがあります。例えば、インターフェースの読みやすさ(良い命名、コメントの充実など)は、理解容易性を支えます。変更の多いインターフェースのラッピングは、安定性を支える実装になります。

テスタビリティの費用対効果

テスタビリティを向上するには、費用対効果を意識する必要があります。費用や効果には、次のような見えにくさがあるためです。

  • テスタビリティの効果は間接的。テスタビリティがテストを支え、そのテストがデバッグや品質保証等のフィードバックサイクルを支えて、やっと効果を発揮する。価値あるテストを支えないと、テスタビリティは価値を発揮できない
  • テスタビリティには他の品質を下げるネガティブな効果がある。具体的には「テスト用インターフェースがセキュリティホールとなる」「テスト用のログ追加で処理性能が悪化する」など。また有限なリソース下では、リソース配分のトレードオフの関係も生まれる
  • テスタビリティの効果は共用や再利用で変動する。一般的に、良いテスタビリティはデバッグのやりやすさにも効果を及ぼす。ユニットテストのためのテスタビリティは、結合テストでも活用可能な場合が多い。テスト工程で有益なテスタビリティは、開発中も継続的インテグレーションやテスト駆動開発といった形で開発者を支える。また、回帰テストなどでは同じテスタビリティを複数回繰り返し利用できる。テスタビリティの仕組みが同じでも使い手によって効果が変わる

テスタビリティの費用対効果を高める

次に、テスタビリティの費用対効果を向上させるアプローチについて解説します。

テスタビリティの費用対効果の向上では「早期から工夫する」ことと「反復的に作りこむ」ことが重要になります。また、前提として「きちんとテストする」ことが求められます。それぞれの要点を解説します。

早期から工夫する:アーキテクチャレベルで工夫する

早期に準備しないと実現できなかったり、費用がかさんだりするテスタビリティは少なくありません。その典型の一つに、アーキテクチャレベルのテスタビリティの設計があります。これは後から変えにくい一方で、結合テストやシステムテストといった規模の大きなテストの生産性を左右することが多いためです。そのため早期から工夫が求められます。

アーキテクチャレベルでよく求められる工夫を次にまとめます。

  • テスタビリティに優れた基盤ソフトウェアの選定。フレームワーク、ミドルウェア、ハードウェアやOSなど、利用する基盤のテスタビリティはテスト対象のテスタビリティに直結する。これらは開発初期に選択が求められ、後から変えにくい。最初の時点でテスタビリティを見極めて選択する必要がある
  • 費用対効果の高いテストツールのサポート。テストツールの導入ではアーキテクチャレベルでもテスタビリティがよく求められる。例えば「エミュレータでのテストを実現するためにレイヤドアーキテクチャを導入し、ハードウェア依存レイヤを一括置換可能にする」といったものだ。そのため初期段階で費用対効果の高いツールを見定め、そのサポートをアーキテクチャに組み込む工夫が重要になる
  • 可変点の配備。テスト対象の切り出し、テスト実行の障害となるコンポーネントを置換するため、可変点の設計が求められる。また可変点はテスタビリティの注入を可能にするため、将来を見越して保険的に配備するのも有効だ。可変点の実現では、アーキテクチャのバウンダリにBrockerパターンやDependency Injectionパターンなどの適応的なインターフェースを使うといったアーキテクチャレベルでの工夫が有効なことが多い
  • 観測・制御の全体整合の確保。漏れや無駄がない効率的な観測や制御ができると、効率的なテストが可能になる。例えば「エラーや例外を網羅的にピックアップできる」「テストが使えるAPIで対象の機能を一通り実行できる」といったものだ。これらの実現には、全体のログ設計といったアーキテクチャレベルでの制御点・観測点の設計が貢献する
  • テスト視点での凝集性の向上/結合性の削減。適切な責務分割は単純性・理解容易性のテスタビリティを高める。また、適切なコンポーネント分割は費用対効果向上策の選択肢を広げる。例えばテスタビリティを確保しにくいコンポーネント、確保しやすいコンポーネント(e.g.GUI依存部と非依存部など)を設計する際は、確保しくにいコンポーネントの責務を減らし、かつ両者の結合性を低くすることで費用対効果を高めやすくできる。これらテスト視点での凝集性・結合性の工夫は、契約による設計と、それをサポートする仕組みの配備をアーキテクチャレベルで推進することが重要になる。「サポートする仕組み」とは、アサーションやセルフテスト、情報隠蔽などの依存性削減、責務分割を破壊する副作用対策といったものが該当する
  • テスタビリティ向上を推進する開発方針の作成。「OSのエラーはすべてログ記録する」「デッドコードを作らない」「特定のコーディング規約を遵守する」といった共通推進すべき開発方針を立てると、プログラマ主導のボトムアップのテスタビリティ向上につながる

早期から工夫する:マネジメントでサポートする

テスタビリティのための早期からの工夫では、2方向からのマネジメントのサポートも重要です。

1つ目は「テスタビリティのニーズ・シーズをつかんで要求に組み込み、テスタビリティを開発して、テストで活かす」というテスタビリティ開発の一連の段取りの工夫です。そこでは今回の解説で挙げた工夫を段取りに組み込みこんでいきます。2つ目はテスタビリティ開発の人材確保といった、リソースマネジメントです。

なお、リソースマネジメントではプログラミング力がテスタビリティの費用対効果を高める基礎となる点に留意が必要です。テスタビリティのほとんどの実現手段はプログラミングであり、プログラミングの生産性がテスタビリティの開発・保守の生産性に直結するためです。特にテスタビリティの保守コストへの影響が大きいです。筆者はコンサルタントとしていくつかの現場でテスト自動化を見てきましたが、保守性の低いコードでテスタビリティを実装した結果、その後の仕様変更への対応コストが悪化し、最終的にそのテスタビリティに依存する自動テストを大きく捨てる結果となった場面が度々ありました。

逆に、優れたプログラマがデバッグ・テスト・保守のやりやすさを考慮して常識的にテスタビリティを確保してくれていたおかげで、費用対効果の優れたテスト自動化を実現できた場面も度々ありました。

プログラミング力の確保では優れたプログラマのアサインも有効ですが、チームのテスタビリティ技術を育てる仕組みも有効です。例えば、ピアレビュー、モブプログラミング/ペアプログラミングのようなプラクティスは、テスタビリティに優れたプログラミングテクニック向上の助けになります。またテスタビリティやテストインフラ構築を主な任務とした開発者ロール(SET、SETIなどと呼称される)を確保して、テスタビリティについて経験やスキルを重点的に蓄積させる施策も有効です。

反復的に作り込む

テスタビリティの費用対効果は、反復的なアプローチで高めやすくなります。その一番の理由は、前述した通りテスタビリティの費用対効果に見えにくいところがあるためです。実際にテストしてみれば、テスタビリティを実現できたか、そのコストは妥当か、どこまでテストに使えそうか見えてきます。すると、費用対効果を高める改善策も見えてきます。

また、テスタビリティが引き起こすブレークスルーの活用にも反復的なアプローチが有効です。テスタビリティの技術蓄積を継続していくと、画期的なテストが導入可能になる場合があります。例えば、開発中の技術蓄積でテスト自動化の範囲が広がった結果、プロジェクト全体で手動テスト主体から自動テスト主体へ戦略を大きく切り替え、テストコストを削減できた事例を筆者は複数見てきました。

そうしたブレークスルーを最大限活かすためには、反復的にテスト戦略を最適化していくアプローチが必要です。逆に、テスト戦略やテスタビリティの仕様を開発初期で固めた結果、テスタビリティの本来の費用対効果を活かせない場面も現場ではよく見かけます。

きちんとテストする

テスタビリティは、価値あるテストを支えてこそ価値を発揮できます。そのため価値あるテストをチームで生み出すことはテスタビリティの費用対効果確保の必要条件です。価値あるテストを生み出すためには、大まかに2つの工夫が必要です。

1点目は、テスト要求をきちんと認識するというものです。開発では、レビュー、静的解析、テスト、防御的設計と様々な品質エンジニアリングの手法を使ってソフトウェアの品質を確保します。その品質エンジニアリングの中でテストが担うべき責務を把握すれば、価値のあるテストとは何か見えてきます。

また、テストの用途は広く、多種多様なニーズ・シーズがどこにでもあります。ニーズ・シーズから、積極的にテスト要求を拾っていくことで、テストが担える価値を広げられます。

なお、テスト要求の分析でもテスタビリティの費用対効果への考慮が重要です。例えばテスタビリティを作りやすいテストの責務を増やし、作りにくいものは他の活動でカバーするアプローチを取ると、テスタビリティの費用対効果の確保が容易になります。

2点目は、テスト要求に対して適切な戦略でテストを作るというものです。例えばジャイルテストのような探索的テスト主体の方針をとるならば、テストエンジニアが必要な知識・スキルを持てるような仕組みが重要になります。大規模なテスト設計が求められるならば、ゆもつよメソッドのような体系だったテスト設計プロセスが求められます。

おわりに

今回はテスタビリティの具体像・費用対効果・作り込み方について解説しました。

テストにコストを掛けているならば、テスタビリティへの投資は生産性向上を支えます。ぜひ今回解説した工夫を通して、テスタビリティ技術を蓄積してみてください。テスタビリティ技術の学習サイクルを回していけば、チームの生産性はどんどん伸びていきます。

テスト自動化研究会 / オリンパス株式会社
コンサルタントとしてテスト自動化を含めた車載ソフト開発の改善を手掛けたのち、現在は医療機器のソフトウェア開発に従事。SET、SETIのチームのリーダとして開発インフラ整備やテスト自動化を推進している。社外では、現在は主にJSTQB技術委員、U30テスト設計コンテスト審査員長などで活動。主な著書に『システムテスト自動化標準ガイド(CodeZine BOOKS)』『Androidアプリ テスト技法(秀和システム)』がある。

連載バックナンバー

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

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

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

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