.NET Framework 4 より、本格的にコードコントラクトがサポートされるようになったそうだ。
とはいっても、実際に検証するのは別のツールであって、コードにコントラクトが書けるように、標準のインターフェイスが準備されたことだ。
System.Diagnostics.Contracts名前空間以下のクラスを使用する。
public int Divide(int a, int b) { // 事前条件 Contract.Requires(b != 0); <pre><code>return a / b; </code></pre> }
要するに、Debug.Assert() していたような所をこれに置き換えていく。事後条件の場合は Contract.Ensures() を使う。戻り値をチェックしたい場合は、Contract.Result() で戻り値が参照出来る。
例えば、負の数が許されない加算であれば、
public int Add(int a, int b) { // 事前条件 Contract.Requires(a >= 0); Contract.Requires(b >= 0); <pre><code>// 事後条件 Contract.Ensures(Contract.Result() &gt;= 0); return a - b; </code></pre> }
という具合だ。
この時点で、「非常に魔術的」な香りが漂っている。特に、事後条件の Contract.Result() で戻り値が参照出来るだとか、そもそも事後条件が除算処理の手前にあったりして、本当にこれでいいのか?という感じがする。
実は、最初にインターフェイスが準備されただけ、と書いたのはこの部分の事で、このコントラクト定義を実際に使うには以下の条件を満たす必要がある。
・コンパイル時シンボル「CONTRACTS_FULL」が定義されていないと、Contractクラスのメソッド呼び出しは全て削除され、バイナリには一切含まれない。
・コントラクトの検証は、能動的には行われない。Debug.Assert() のように、書いた場所で直にコードが確認するわけではない。
実際には一部の評価は行われるが、結局のところは「バイナリリライター」と呼ばれるライブラリが介在する必要がある。
で、それでは、Debug.Assert() と比べて何が嬉しいのか?という話になるのだが(ようやく本題)、コードに書かれたコントラクトをメタデータ(とMSIL?)から抽出して、その情報を元にごにょごにょ出来るようになるというところが違う。
Microsoftの実装では、「Pex」というプロジェクトでコントラクトを利用している。Pexは、ユニットテストコードを自動的に生成するツールとライブラリで、コードからコントラクト定義を抽出して、事前・事後条件などからユニットテストが実行すべきテストパターンを計算、「テストコード」を吐く。
恐ろしいww
いや、もちろん、完全な自動化テストは不可能ということは分かっているのだが、特にこの場合はどれだけコントラクト定義が綿密に書けるかということと、いかに普段からテスト可能コードを書けるかと言う事に尽きるのだが、それにしてもとうとうやっちまったかという感じがする。
PexはVS2010のProfessional以上で使用可能で、MSDNサブスクライバダウンロードから入手可能。また、Ultimateでは標準で入っているので、Microsoftとしてももはやこのプロジェクトは実験段階を超えていると判断しているようだ。
より詳しく知りたい場合は、以下のサイトを参照されたし。
MSDNマガジン:クラスにソフトウェア コントラクトを導入する
MSDNマガジン:Visual Studio 2010 における Code Contracts の設定
Microsoft Reserch: Pex and Moles (なにげにMolesも凄いのだが)
VS2010 後の世代のプログラミング (2) Pex
他にも、コントラクト定義を使ったこんなのもある。
Contractor.NET | A .NET validation and specification strengthening tool
.NET Framework 4 未満の互換ライブラリを書いているのであれば、ここにある前身バージョンを使えば、コンパイルは通るようになります。
(.NET Framework 2 からOK。ただし、バイナリリライターはないので、検証は出来ません)
これは良いですね。これは早速使ってみよう。
(逆に)自動的抽出されたユニットテストコードの量と質を見れば
コードの出来(仕様理解度)がわかるかもしれないですね。
これ、ユニットテストの論理的なリファクタリングっぽい作業になるんですよ。
まず、Pexでユニットテストコードを吐かせます。そうすると、
1.出来たテストコードを見て、想像した網羅性との食い違いを見ることが出来る。
→足りないコントラクトとは何かを検討して、コントラクトを追加してPexで再生成。
テストを実行して、カバレッジで埋まらない部分を狭めていく
2.上記を繰り返して、それでも埋まらない部分が実装の本質的な部分。
→その部分だけが、コントラクトで表現の難しい領域。
それを「コントラクトで表現可能な実装に置き換える」か、
「従来通り真面目にテストコードを書くか」という感じ。
コントラクトで表現可能な実装に置き換えることが出来ると、実はアルゴリズム的に
シンプルになったりして嬉しい。
って、言うほどまだ慣れてないんですが…
# 今の出先が、ユニットテストどころかVBチックな設計しか出来ない、某T子会社なので
# 実務で生かせそうもない orz
# いまどきインターフェイス分析もクラス設計も無いなんて勘弁してくれ