ソフトウエアの単体テスト

October 06, 2017

プログラミング歴は非常に長いのですが、正直テストについてはあまり真面目に取り組んで来ませんでした。品質を上げるためには必須の工程であることは理解しているつもりですがなかなか時間が取れず、簡単な動作確認で済ましてしまうことが多かったです。しかし、自分しか使わないようなソフトなら不具合が見つかれば随時デバッグ、ということが許されますが、顧客に使っていただく場合は当然品質を保証することが求められますし、出荷した後のアップデートが困難(例えばネットに対応していない組み込み機器)だと、製品を引き取って修理する羽目になりますので、テストは避けて通れません。というわけでたまにはソフトウエアテストについて考えてみようと思います。

ソフトウエアテストはテスト対象によってある程度分類されていると思います。教科書には、単体テスト、結合テスト、総合テスト、という風に分けられていて、段階的に進められる、と書いてあることが多いように思います。人によって、これらの言葉をどう捉えるかは様々だと思いますが、今回は単体テストについて、自分が思っていることを述べます。

そもそも単体テストとは何か、という議論があるかと思いますが、例えばWikipediaには、「ソースコードの個々のユニット、すなわち、1つ以上のコンピュータプログラムモジュールが仕様に適しているかどうかを決定するために、関連する制御データ、使用手順、操作手順とともにテストする手法である」とあります。ここで疑問なのは、モジュールという単位がどのくらいの粒度のものなのか、ということです。1つのクラスなのか、関数なのか、あるいは、外部に公開しているAPIなのか。ググってみた印象では明確に定義されていないような気がしていますが、クラス、あるいはメソッド単位で行うテストとして扱うケースがよくみられますので、このようなテストを単体テストと見做すことにします。

非常にシンプルなケース、例えば、関数に与えられた引数から一意に決まる戻り値が正しいかどうかをテストする、なら話は簡単なのですが、Webアプリケーションのような複雑な構造を持つソフトウエアだとそう簡単にはいきません。様々なクラスが連携して動作しますし、データベースへのアクセスや外部サービスとの連携を伴いますので、スタブ、ドライバ、テスト用データの準備が必要になります。外部サービスとの連携については、スタブの作成には外部サービスの仕様を参照して作成することになるので、それなりに手間がかかりますし、バッチなどで取得した大量のデータから統計情報を計算するようなアプリケーションだとデータを準備するだけでも大変です。

また、時間に依存するようなアプリケーション、例えばある日時になったら指定された処理を行うようなアプリケーションのテストも厄介です。無理やりシステム時間を変更してテストする方法もありますが、その場合はVirtualBoxなどを用いた仮想環境下で行うか、テスト専用のPCを用意するなど、専用のテスト環境を準備することになると思います。あるいは、テスト対象となるアプリケーションのプログラムで日時を参照する部分を直接システム関数などを参照するのではなく、外部から渡されたインスタンスを介して参照するように実装することで、実稼動時とテスト時で処理を切り替えれるようにすれば、上記のようなテスト用の特殊環境は不要になります。

このように開発者に負担のかかる単体テストはきめ細かいテストができますが、バグが完全になくせるわけではありません。外部サービスとの連携を行うような場合、想定していたインターフェイス仕様が実は違っていたとか、環境によっては発生してしまう環境依存の不具合など、実際の稼働環境でないと発見できないバグも多々あります。また、単体テストは基本アプリケーションのソースの構造を熟知していないとできませんので、ソースを実装した開発者自身が行うことが多いと思うのですが、開発者の思い込みによりテスト結果の期待値自体が誤ってしまう、といったことも起こりますので、テストやデバッグ後の修正確認は開発者ではなく、テスト専任者が客観的に行うのが理想です。

以上のように、単体テストを行うにはかなりの工数がかかる上に、不具合を完全に発見できるとは限りません。そのため、スケジュールがタイトなプロジェクトになると、どうしてもテストにかけることのできる工数が少なくなり、特に単体テストはおろそかになってしまうと思います。実際の現場では、最初に出てきた仕様書が開発期間中に何度も変わってしまうことも多く、その度に単体テストをやり直すとなるとスケジュールに入らなくなります。

そのため、もし単体テストをやるならば、不具合が発生した場合の致命度が高いとか、総合テストでのテストが難しい、仕様が複雑でバグが出やすそう、といったような、単体テストを行う意味があるようなケースのみをピンポイントでテストするべき、と考えます。昔、ソフトウエアテストに関するセミナーを受けた時に講師の方が「網羅とピンポイント」、と繰り返し言われていたのですが、確かにその通りだと思います。テストの重複は避けつつ、漏れがないように、ピンポイントで行う、というのがテストの鉄則なんだと思います。