.NET Tools

テスト駆動開発ツール最前線(前編)

− .NET用テスト駆動開発支援ツールNUnit 2.2の新機能 −

(株)ピーデー 川俣 晶
2004/11/06
Page1 Page2 Page3

前置き

 この記事は、「NUnit入門 Test Firstのススメ [NUnit 2.0対応版]」および「『テスト駆動開発』はプログラマのストレスを軽減するか?」の内容のアップデートという位置付けである。今回は、それに加えて関連する2つのツールを紹介する。NUnitとは何か、テスト駆動開発とは何か、という詳しい説明は繰り返さない。それについては、上記2つの記事を参照願いたい。

プログラム開発の効率をアップするための方法

 プログラム開発の効率を上げるために、最も効果があることは何だろうか。いろいろな考え方があると思うが、設計、コーディング、テスト、デバッグといった大ざっぱな工程を思い浮かべたとき、デバッグこそが効率アップの対象になりそうだな、という印象を持つことができる。設計、コーディングはプログラムを生み出すうえでの必須の工程なので、除去はできない。また、テストは品質保証上必要とされる工程である。それに対して、デバッグは、バグさえなければ発生しない工程なのである。

 しかし、これは机上の空論に思えるだろう。人間は間違いを犯す生き物であるから、バグのないプログラムなどというものを作ることは不可能だ。しかし、そう思ってあきらめてしまうのは建設的ではない。バグがないプログラムはあり得なくても、バグの早期検出、早期撲滅の可能性を高める方法は確かにあるからだ。

 例えば、Visual Basicは、もともと変数宣言を必須とせず、変数の型を指定することも必須ではなかった。しかし、Visual Basic .NETへ進化していく中で、従来のようなプログラミングも不可能ではないが、デフォルト設定は変数宣言を必須とし、さらに厳密な型指定まで要求するオプション(Option Strict)が追加されている。なぜこのような宣言の厳密化が進んでいるのかといえば、その理由の1つがコンパイル時にバグが検出される可能性を高めるためだ。つまり、バグの早期検出である。そして、コンパイル・エラーが出れば実行できないので、自動的にバグの早期撲滅が行われる。コーディング時に以前は必要がなかった文字をタイピングさせられて効率ダウンだと思う人もいると思うが、バグの早期検出、早期撲滅が可能になることで、手間の増加よりもはるかに大きな効率アップを得ることができる。

 しかし、変数や型の宣言を必須にしても、それで検出できるバグはごく一部のものである。これだけでは、劇的な効率アップは望めない。ほかにも、効率を上げる手段はないのだろうか。

 変数や型の宣言を行うことは、いい換えれば、コンピュータにプログラムの正しさを判定するヒントを与えることにほかならない。そう考え、より多くのバグを早期検出させたいと思うなら、コンピュータに与えるヒントを増やせばよいことになる。

 そして、そのためのヒントの1つが、最近はやりのテスト駆動開発でいう単体テストである。単体テストは、テスト・プログラムによりクラスやメソッドなどを個々に実行して結果をチェックする。クラスやメソッドが正常に動いているかどうかを、コンピュータが自らチェックできるプログラムを用意すれば、バグの早期検出は可能になる。

 そして、効率よく単体テストを実行するために、テストのフレームワークと呼ばれるソフトウェアが使用される。これを使って何度も繰り返しテストを実行することにより、ソース修正でバグが混入しても、早期検出される。今回取り上げる「NUnit 2.2」も、そのようなフレームワークの一種ということになる。そして、次回後編で特に大きく取り上げる「Mockオブジェクト」という手法は、プログラムの正しさをコンピュータに伝える有力な方法の1つということになる。

プログラミングにつきまとう「あいまいの罠」「厳密の罠」

 さて、ここで注意が必要であるのは、プログラムの正しさに関するヒントをなぜプログラムとして書かねばならないか、である。これまで、クラスやメソッドの仕様を厳密に定義する文書を作成したプロジェクトも多いだろう。そのような仕様書をプログラムの正しさのよりどころにすることはできないのだろうか。

 残念ながらそれはできない。なぜなら、それは「あいまいの罠(わな)」に落ちてしまうからだ。仕様書といえば、日本語や英語のような自然言語で記述するものが多いが、それらは2つの点で、プログラムの正しさを示すには十分ではない。

 1つは、それ自身が多くのあいまいさを含んでいることである。自然言語はもともとあいまいさを含むので、いくら厳密に書こうとしてもあいまいさが残ってしまう。仕様書に従ってプログラムを書いていて、いくつもの解釈ができたり、矛盾していて書けなかったりする状況に遭遇した経験があるプログラマも多いと思う。それは、自然言語で仕様を書くことによって必然的に発生する問題である。

 もう1点は、自然言語はプログラム言語と違って実行できないことである。実行できないということは、バグの早期検出に使えないだけでなく、いくらでも読み手の恣意(しい)的な解釈によって左右され得る可能性を持つ。例えば、ISOやW3Cなどの同じ標準仕様に基づいて作業しているにもかかわらず、異なる挙動のプログラムが書かれてしまうような状況は、しばしば発生している。つまり、自然言語があいまいなだけでなく、読み手の人間もあいまいなのである。このような、あいまいな言葉を多用して自ら無意識的に問題を増やすことが「あいまいの罠」である。IT業界には、自らそれと意識せずに「あいまいの罠」に落ちている人は、かなり多いと感じる。単体テストをプログラムとして書けば、自然言語のあいまいさも入り込まないし、実行すれば結果は(たいていの場合)1つに定まり、人によって解釈が変わることはない。

 しかし、あいまいさが悪であると考えて、あらゆるあいまいさを取り除こうとすると、逆に「厳密の罠」に陥る。ここでは、プログラム開発の効率アップのために、バグの早期検出、早期撲滅が有効であるという話をした。もし、プログラムの正しさに関するヒントをより厳密かつ完ぺきに記述できれば、検出漏れとなるバグが減り、より一層効率が上がるように思えるかもしれない。しかし、その考えは正しくない。なぜなら、厳密度を上げていくと、かえってバグの早期検出ができなくなってしまうためだ。

 そんなばかなと思うかもしれないが、テストの作成や実行には時間がかかるのである。例えば、テストの実行が30秒で終われば、ソースを1カ所修正するごとにテストを実行できるだろう。もしも、修正にバグが含まれていれば、そこで検出される。まさに早期検出である。修正した個所は1つしかないので、バグが含まれている個所もすぐ分かる。しかし、テストの実行に30分かかるとしたらどうだろうか。ソースを1カ所修正するごとに30分待たされては仕事にならないので、後でまとめてやろうと思うかもしれない。しかし、後でまとめて実行したのでは早期検出にならない。ソース数十カ所を修正した後にテストでバグが発覚したとしても、どの修正個所が原因かすぐ分からないかもしれない。これで、早期撲滅もできなくなる。

 理論的に完全なる厳密さは実現できないという話は別としても、厳密さを高めすぎることは、かえって効率を落とす結果になる。これが「厳密の罠」である。IT業界の中でも、特に頭の回転が速い優秀な人たちの中に、このような「厳密の罠」にとらわれている人たちを見かけることがあるように感じる。

 この2つの罠、「あいまいの罠」と「厳密の罠」に落ち込むことなく、目的を正しく見据えて、プログラム開発の効率アップを目指そう。今回の内容はそのためのヒントである。

NUnitと新バージョン2.2

 すでに述べたように、NUnitは、単体テストを実行するフレームワークである。Java用のフレームワークであるJUnitからさまざまなプログラム言語や環境のためのフレームワークが生み出されているが、その中の.NET Framework用のものがNUnitとなる。とはいえ、ご先祖といえるJUnitとはかなり違った進化を進んでおり、内容的にはかなり異なるものになっている。また、NUnitは特に対象となるプログラム言語は決まっておらず、.NET Framework上のさまざまプログラム言語で使用できる。C#やVisual Basic .NETでの利用はまったく問題ない。

 さて、今回の記事はNUnit 2.2を取り上げるが、NUnit入門 Test Firstのススメ [NUnit 2.0対応版]の続編という位置付けもあるため、対象として1つ前のバージョンであるNUnit 2.1も含め、2.0からの変更点を扱ってみたいと思う。

 NUnit 2.1の主な変更点は以下のとおりである。

  • Visual Studio .NET 2003/.NET Framework 1.1対応
  • TestFixture属性に含まれるテストが実行される前後に、1度だけ実行されるメソッドを指定するTestFixtureSetUp属性、TestFixtureTearDown属性の追加
  • Assertionクラスの代わりに使われるべきAssertクラスの追加
  • テスト失敗やエラーについて、より多くの情報をレポート
  • Windows 98/Meのサポート

 そして、NUnit 2.2の主な変更点は以下のとおりである。

  • .NET Framework 2.0/Mono 1.0対応
  • 異なる.NET Frameworkバージョン用に個別のconfigファイルが不要
  • NUnitフレームワークとコア(実際の処理を行う中枢部分)が分離され、GAC(グローバル・アセンブリ・キャッシュ)にインストールされる
  • Assert.AreEqualメソッドが配列を比較できるように拡張された
  • オプションのメッセージ引数を持つすべてのAssertメソッドが、フォーマット(書式整形)に使用するためのオブジェクト配列を持てる
  • 動的にテストを無視させるAssert.Ignoreメソッドの追加
  • TestFixtureSetUp属性またはTestFixtureCleanUp属性の付いたメソッドの失敗はエラー扱いとなり、テストを無視させない
  • CategoryAttribute属性によるテストのカテゴリ分けとカテゴリごとのテスト実行
  • 選択された場合にのみ実行されるテストを示すExplicitAttribute属性の追加
  • 組み込み可能で軽量のMockオブジェクトのサポート

 これらの変更点のうち、実際にテストを記述する際に重要となるポイントについて次に見ていく。

 

 INDEX
  [.NET Tools]
  テスト駆動開発ツール最前線(前編)
   1.プログラム開発の効率をアップするための方法
     2.GUIテストランナーとテストのための新機能(1)
     3.GUIテストランナーとテストのための新機能(2)
  テスト駆動開発ツール最前線(後編)
     1.Test Driven .NETによる統合されたテスト
     2.Mockオブジェクトとは何か− NUnitのMockオブジェクト
     3.より高度なMockオブジェクトを実現するNMock
 
インデックス・ページヘ  「.NET Tools」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間