(この記事は、開発元Parasoft社 Blog 「The Value of Using a Unified C/C++ Testing Tool」2018年5月17日の翻訳記事です。)
検証/妥当性確認 (V&V) とソフトウェア開発は切っても切れない関係にあります。特定のプロジェクトのV&Vに投入される作業量と予算は、プロジェクトの機能安全目標、ビジネスリスクのレベル、品質に関する組織の文化など、多くの要因によって決まります。組織が品質向上策とプロセスを推進する動機が何であろうと、安全で高品質なソフトウェア製品を生み出すには、ただの決意以上のものが必要です。
適切なテスト手法を選択することは、多くの理由から困難な課題です。テクノロジーは急速に進化しており、企業は採用するソフトウェアテストツールを選択する必要があります。多くの場合、オープンソースか商用製品かの選択も難しい問題です。
この記事では、高度な静的解析、実行時メモリ監視、自動ユニットテスト、フロー解析などの自動化されたテスト手法を組み合わせて品質保証プロセスを改善する方法、そして統合テストツールを利用する利点について説明します。ここで説明するコンセプトは一般的なもので、どのプログラミング言語にも適用できますが、ここではParasoft C/C++testを使用してCおよびC++言語で開発するケースを念頭に置いています。
欠陥の分類とツール自動化
ソフトウェアの不具合を高いレベルで考えると、エラーはいくつかのクラスに分類できます。つまり、要件不足に起因するエラー、誤って指定された要件によるエラー、要件が正しくコーディングされていないことによるエラーなどです。このうち最初の2つのクラスは、要件エンジニアリングのカテゴリに分類されるので、ここでは説明しません。ここでは、誤った実装によるエラーに焦点を当てています。これには、多くのチームが苦労しているさまざまな潜在的なソフトウェア問題が含まれます。
ではまず、要件が正しくコーディングされていないとはどういう意味でしょうか?これには、多くの可能性が考えられます。たとえば、要件の実装の誤りであれば、正しく実装されているかをチェックするよう設計された単体テストが失敗するので、テスト自動化ツールによって欠陥を検出してレポートできます。別の例としては、実行時解析ツールは、単体テスト実行時に、単体テストの結果だけからは検出できない深刻なメモリアクセスエラーを検出できます。次の図に示すように、さまざまなツールがあり、それぞれ特定のクラスのエラーを検出するのに優れています。
図1. ソフトウェアの欠陥と検出方法の見取り図
当然ながら、すべてのプロジェクトで、ソフトウェア品質のテストと改善に利用可能なあらゆるテクノロジーを使用する必要はありません。組織は常に、どのように予算と品質のバランスをとるかというジレンマに直面しています。機能安全に関連するプロジェクトは、妥協のない品質を保証するために、またISO 26262などのソフトウェア安全基準への準拠を保証するために、利用可能なすべての技術を選択する傾向があります。そうでないチームでは、静的解析と単体テストだけでもソフトウェア欠陥のかなりの部分をカバーしているように見えるので、この2つだけを選択することもあるでしょうし、一部のチームではオープンソースの単体テストフレームワークでじゅうぶんでしょう。
なかなか幅広いテスト技術が採用されない大きな理由としては、複数の技術を使用すると、予算はもちろん、開発のペースにかなりの負担になるのではと懸念されることが挙げられます。統合されていないツールを選択しようとしている場合、この懸念は現実のものである可能性が高いでしょう。複数のばらばらのツールの費用、学習曲線、さまざまな利用スタイルとインターフェイスを切り替える必要性があることなどが問題として考えられます。その結果、コードを書くことからツールの使用に気が逸らされ、生産性が低下するために、開発者がツールや自動化を忌避する可能性があります。
統合テストツールの価値
統合テストツールの価値を議論する前に、テストツールに求められる要件を定義することが重要です。ツールは、理想的には以下の特徴を持つべきです。
- 複数のテスト技術をサポート
- 使いやすい
- 機能上の問題および以前はなかった不具合を検出する
- 要件からテストまでのトレーサビリティを提供する
- コードの複雑度、移植性、保守性を測定する
- コード作成中に即座にフィードバックを提供することによって開発者を教育する
- 開発の進行状況に関する情報を提供する
- さまざまな手法の結果を組み合わせて高度な分析を可能にする
統合テストツールは、以下のような問題を回避するのに役立ちます。
- インターフェイスが異なる別個のツールを使用することによる学習曲線とユーザビリティの問題
- 開発者がコードを書く邪魔になる
- ツールチェーンのさまざまな要素間で情報を交換できない
可能なかぎり多くの欠陥を検出する
ソフトウェアの欠陥はさまざまなカテゴリにわたるため、すべてを1つのテスト手法で検出できるとは期待できません。たとえば、手動でのシステムレベルのテストを考えてみましょう。具体的には、次のコード例を見てください。
上記のような短いコードにも、いくつか問題が含まれてます。特に、16行目では、calculateIdx()関数で計算されたインデックス値を使用してグローバルバッファーを初期化しようとしていますが、インデックスが許容範囲内であるかどうかを検証していません。手動テストセッションを何回も実行したとしても、この問題を検出できるとはかぎりません。なぜなら、整数値をランダムなメモリ位置に書き込んでも、ただちに分かるような結果が出ることはめったにないからです。
しかしある日、おそらくはリリース後に、メモリレイアウトが変更され、16行目の操作がアプリケーションをクラッシュさせる可能性があります。興味深いことに、静的解析でも、インデックスを計算するのに使用されるループが原因で、この問題を検出することはできません。パフォーマンス上の理由から、静的解析ツールで使用されるデータおよび制御フロー解析では、ヒューリスティックや単純化を用いて妥当な時間内にコードの解析を終わらせるため、ループを解析したときにエラーを見逃してしまうことがよくあります。
ソースコードに特別な検査用コードを注入し、不適切なメモリ操作を報告するメモリ監視ツールであれば、このエラーを確実に検出します。実行時解析ツールは、推測なしで、実際に実行されたパス上の問題だけを検出するため、報告された問題の精度は非常に高いものになります。
この例では、Parasoftのメモリエラー検出によって問題が簡単に検出されました。
逆の状況もじゅうぶんにあり得ます。実行時メモリ監視で識別できない問題が、静的解析で検出されることがあります。たとえば、下のコードスニペットの27/28行目には、nullポインターの間接参照の可能性があります。personポインター引数がnullの状態でstorePersonToFile関数を呼び出すとエラーが発生しますが、これはretrivePersonFromDB関数がnullを返す場合にのみ発生します。このような状況は、システムテストセッションではまず発生しないでしょう。データベース接続が予想どおりに機能する可能性が高いため、実行時メモリ監視ツールは役に立ちません。しかし、静的解析ツールのフロー解析機能は、この関数から返されるnullポインターを簡単に検出し、潜在的なnullポインター間接参照の問題を報告します。
図4. 静的解析結果の例
機能的問題および以前はなかった不具合の検出
常にエラーなく実行できるが、要件を満たしていないコードはどうでしょうか?静的解析および動的メモリ解析は、この種の問題を検出するのには役立ちません。期待される結果と実際の結果との相違を検出するには、計算結果とあらかじめ指定された値との比較が必要です。このようなチェックを行う方法としては、手動システムレベルテスト、統合テスト、ユニットテストなど、多くの一般的な方法があります。
統合テストツールはさまざまな面でユニットテストプロセスを容易にします。たとえば以下のようなメリットがあります。
- テストケースの作成、編集、および設定のためのグラフィカルなウィザードを提供するなど、テストケースの作成時に開発者の生産性を向上させます
- Test Doubleフレームワークとの統合により、大規模なコードを必要とせずに、簡単にモックとスタブを利用して複雑なテストシナリオをシミュレートすることを可能にします。
- テストケースとコードカバレッジレポートを関連付け、テストの網羅性を評価するのに役立ちます
- コードの差分を検証するのに必要な最小限のテストケースを自動的に計算してテストセッションを最適化します
- マニュアル/システムレベルのテストや統合テストなど、異なるテスト手法のコードカバレッジレポートをマージして、テストされていないコードを特定します
- テスト結果から要件へのトレースを提供し、失敗したテストの影響を把握するのに役立ちます
グラフィカルなウィザードやエディターを使用してテストを作成することで、QAチームが単体テストの作成プロセスに携わり、開発プロセスに積極的に貢献できるようになり、組織全体の生産性が向上します。QAチームのメンバーにとっては、エディターでテストコードを書くよりも、グラフィカルなウィザードを使用する方が簡単です。特に、チームメンバーにあまり経験がない場合、下の図のように入力フォームを使用して値を設定する方法であれば、高度なコーディングスキルは必要なく、時間がかかりません。たとえば、下のスクリーンショットは、Parasoft C/C++testのユニットテスト機能の1つ、ユニットテストの作成に役立つウィザードを表しています。
ツールを使用するもう1つの利点は、テストの網羅性や、重要なビジネス要件が満たされているかに関してフィードバックを与えてくれることです。ステートメントカバレッジの値が高くても、 MC / DCカバレッジの値が低い場合、ブランチカバレッジが不十分である可能性があります。この種の分析では、複数のカバレッジメトリクスをサポートし、チームが行カバレッジやステートメントカバレッジなどの簡単なものから始められ、テストケースの改善とともにより完全なコードカバレッジを進めることができるツールチェーンが必要です。
要件からテストへのトレーサビリティ
ユニットテストとシステムテストのカバレッジレポートは、特にこれらを組み合わせたとき、テストプロセスに関する大きな情報源となります。しかし、テスト結果が要件と関連付けられていない場合、いくつかの重要な情報が欠けています。テストから要件へのトレーサビリティレポートを表示することで、要件カバレッジの状況を迅速に判断できます。そのようなレポートの例を次に示します。
さまざまな種類のテストの結果と要件を関連付ける機能は、統合テストツールを使用する大きな利点です。ユニットテスト、システムテスト、統合テストの結果、そしてコーディング規約チェックおよびコードメトリクスの結果を相互に関連付けて、クリティカル要件および非クリティカル要件の健全性に関するフィードバックを提供することができます。
さまざまな手法の結果を組み合わせて高度な分析を行う
プロジェクトで使用されているさまざまなテスト手法のデータを結合すると、さらに高いレベルのメトリクスと高度な分析を実現できます。統合テストツールを使用すると、チームはまったく新しい視点からコードを見ることができます。ユニットテストケースの失敗 1つにしても、コードメトリクス分析で高リスクと分析されたコードで発生したか、低リスクのコードで発生したかによって、異なる意味を持つ可能性があります。さらに、この情報がソース管理システムの情報と結合され、要件に関連付けられている場合、チームはコードをいつどのように修正するべきかについて、より適切な判断を下すことができます。
Parasoftツールスイートを使用すると、変更ベーステスト機能を活用してチームの生産性を向上させることができます。変更ベースのテスト技術は、ソースコード、テストケース、およびコードカバレッジ結果の関係を捉えて、特定のコード差分の検証と妥当性確認のための最適なテストケースを計算します。つまり、回帰テストスイート全体ではなく、小さなサブセットのテストのみを実行することで、テストセッションの範囲を狭めることができます。絶対に必要なものだけをテストすることに集中すると、特に中規模または大規模プロジェクトの場合、大幅な省力化につながります。
すべてを1つに:Parasoft C/C++testとDTP
これまで見てきたテスト技術はすべて、 Parasoft C/C++testとParasoft DTPで利用できます。 Parasoft C/C++testは、CおよびC ++プロジェクト向けの統合テストツールです。C/C++testは、EclipseやVisual Studioなどの一般的なIDEのプラグインとして利用できます。IDEとの緊密な統合は、前に述べたような問題を防ぎます。開発者は、コードの作成時にコーディング規約への準拠をチェックしたり、ユニットテストを実行したりできます。QAチームのメンバーは、手動テストシナリオを実行するときに、アプリケーションのコードカバレッジと実行時エラーをモニターできます。静的解析ツールによって提供されるフロー解析などのオフライン解析は、継続的インテグレーションの一部として実行できます。多機能なコマンドラインインターフェイスは、GUIを起動しないサーバーセッションをサポートしており、解析結果をParasoft DTPに報告します。Parasoft DTPは、開発環境からの情報を集約します。Parasoft DTPは、サードパーティのツールからデータを統合し、テスト結果を開発者のIDEに送信し、高度な分析を行うなど、広範なレポート機能を備えています。ワークフローの各段階で継続的なフィードバックを提供することで、アジャイル開発が加速され、安全規格への準拠を達成するコストが削減されます。
ここで説明した品質保証プロセスおよびアーキテクチャへのアプローチは、C/C ++プロジェクトに限定されないことにも注意してください。Parasoftは、JavaおよびC#/.NETでも同様のソリューションを提供しています。
まとめ
ソフトウェアの品質向上は、ただ1つの要素で達成できる目標ではありません。さまざまな種類のソフトウェアの欠陥を根絶するためにはさまざまな技術が必要です。そこで大きな課題となるのは、いかに効果的かつ効率的に、プロジェクトの予算を圧迫したり、開発者の士気を損なったりすることなく、この目標を達成するかです。開発者のIDEに緊密に統合されたテストツールは、最高に生産的な開発テスト環境を提供します。Parasoft C/C++testなどの統合ツールは、メトリクスとあらゆるテストの結果を統合する機能を備えているため、リスクの高い最近変更されたコードにテストを集中させることができます。