大鳴(おおなる)と申します。
初投稿です。よろしくお願いします。
先日vbUnitをちょっと試していたときに気付いたことで、
スレッド「幻覚剤としてのテストファースト」([XP-jp:04130]〜)
スレッド「浮動小数点演算や幾何演算のテスト」([XP-jp:04197]〜)
で触れられていた話題と関連があると思ったので投稿します。
振り返るとすごく普通な結論である気もしている上、
実はもう議論し尽くされた話題
(あるいは単にDbCを理解しなおしているだけ)のような気もしていますが・・・。
やったことというのはこうです。
・ごくごく簡単なクラス用のごくごく簡単なテストを作って、各featureを実装
・VBだけどちょっとDbCしてみようと、Require/Ensureプロシージャを作成、
featureに適用
・実はテストしていたことがpostconditionで述べられてしまっていることに気付く
・なのでテストケース中の
AssertXxx(vbUnit3ではVerifyなんですが)にあたる部分を削除
・するとテストケースの中は具体的なインスタンスを
具体的に動かしてみているだけになってしまった
このときこう思いました。
DbCは各class/各featureを公理的にcharacterizeしようとしている以上、
それができてしまったらユニットテストでチェックすることなんか
なくなってしまうのが当然じゃないか、と。
しかし、と思い直しました。
そのcharacterizationは形式的な言葉で述べられているため、
それが「正しい」保証というのはなくて、
それを具体的なテストケースで試しているんだとしたら、
やっぱりこうしたテストケース/テストには意味があるんだ、と。
ところがここで、そのテストケースたちが一体何をテストしているのか、
ということについて、(少なくとも私には)重要なシフトが起きていたと感じました。
当初、登場するのは2項
a) class/featureの実装
b) 実装者の「意図」(かなり素朴な物言いですが)
で、a)がb)にマッチしているかどうかを具体的なテストケースでテストする、
という認識でいました。
しかし、今や、登場するのは3項
a) class/featureの実装---"how"
b1) class/featureのcontract---"what"
b2) 実装者の「意図」
で、もともとb)だったものがb1)とb2)に分かれて、
AssertXxxでテストすべきなのは
b1)がb2)にマッチしているかどうかだ(つまりもうa)ではない)、というシフトです。
(なんですが上記の例で私が最終的に「テスト」していたことは下記の方です)
では、a)とb1)の整合性は疑わなくていいの?というとそういうわけではなくて、
いつの日にか自動証明ができる日が来るかもしれませんが、
現実にはそのテストも必要なわけです。
(なので、b1)からa)がほとんど自明になってしまえば理想的かもしれません。※)
ただし、その場合、「contractに反している=exceptionが発生する」なので、
DbCしている状況ではテストケース側でAssertXxxするというのは方法がずれていて、
「AssertXxxのないテストケース」で行われるのが自然に思えるのです。
(一方、b1)とb2)の整合性チェックではAssertXxxは意味があります。)
(少し余談・・・※も参照してください)
ここで、a), b1)はシステム側で表現されている部分
(「構造化されている」と言ってもいいかもしれません)で、
b), b2)はそこにすくいきることのできない、
まだ構造化されていない何かと仮定される「現実」です。
恐れず言ってしまうと、ラカンなら「対象a」と言っていたであろうもの、
我々ならたとえば「要件」という構造になんとか回収しようとしている何かです。
(「実装者の意図」と言っているのに「要件」を持ち出すのは
適切でないかもしれませんが、
ある意味実装者はそのモジュールに何かcharacterizationを与えようとして
自分で自分の「要件」を形にしていっている、とは言えないでしょうか。)
これは、「構造」の中での「完全」なテスト(たとえばa)とb1)間)が不可能だ、
というのとは別次元の話で、
XPが「要件は変化するもの」と悟ってしまった部分です。
形式的(=演繹的=内包的=面的)に述べ得たと思った瞬間に
それは構造側に去っていってしまうので、
それが本当に正しいのかどうかはせいぜい具体的(=帰納的=外延的=点的)な
テストケースで確認するくらいしかできない、というところでしょうか。
しかし、そうしたテストケースを上手に抽出できることは、
よりよいcontractを言い当てるのにとても効果的であるとも思います。
直線という無限の集合をたった2点でcharacterizeしてしまうように。
(それが直線でよかったのかどうかはついに分からないのですが。)
また恐れずに言ってしまうと、
Kent BeckがTDD本で"triangulation"という名前を与えているのは
こうしたcharacterization法ではないかと思います。
・・・以上のようなお膳立てで2つのスレッドの内容(のごく一部)を
読み返してみると・・・
「幻覚剤としてのテストファースト」:
[XP-jp:04130][04146][04162](大村さん)
a)とb)の整合性をチェックするのに具体的なサンプルをいくつか試すだけでいいのか
?
そのサンプルたちはb1)ではない。
[XP-jp:04133](太田さん)
(除算の例はpreconditionも考慮に入れないといけない、
というサンプルになっていると思いました。)
Kent Beckはb2)からのすくいもれは「経験によりカバーできる」と書いている。
[XP-jp:04135](小島さん)
b2)(あるいはb))は構造側には決して回収できない。
[XP-jp:04137](濱井さん)、[04140](渋川さん)、[04144](大村さん)
テストファーストの「テスト」は(b2)とb1)をつなぐものなので、でしょうか)、
「テスト」というよりも「(実行可能な)仕様記述」である。
(「テスト」と言ってしまうと
形式的(=演繹的=内包的=面的)な意味を指向してしまうからでしょうか。)
[XP-jp:04138][04154](濱井さん)
b1)は構造側である。
[XP-jp:04153](宮田さん)
b2)とb1)+a)の整合性は機能テストで行うことである。
[XP-jp:04158](安井さん)
b2)(あるいはb))をできるだけ構造側に回収できるように設計している。
[XP-jp:04165](濱井さん)
b1)を仕様と言い、b2)からb1)を生み出すことを設計という。
[XP-jp:04171](安井さん)
b1)からb2)の具体的な「テスト」ケースを導出するツールを考えている。
「浮動小数点演算や幾何演算のテスト」:
[XP-jp:04238](上原さん)、[04250](平鍋さん)
b2)とb1)(+a))の整合性をチェックするユニットテストは
具体的(=帰納的=外延的=点的)、
内部表明〜contractは形式的(=演繹的=内包的=面的)。
両者はそもそも異質なもので、併用すべき。
・・・かなり自分に都合のいいように強引に解釈していると思います・・・。
誤解のご指摘をいただけるとありがたいです。
大鳴
(※)
今Bertrand Meyer氏のOOSC2を読み返していたら、
"specification"、"design"、"implementation"、
という切り分けが触れられているところがありました(p.150)。
specificationからdesignへの移行とはimplicitからexplicitへの移行、
designからimplementationへの移行とは(ともにexplicitであるのだが)
abstractからconcreteへの移行、
そしてOOというのは、ほぼdesignとimplementationの間の区別を取り去ることなん
だ、
と。
だから、うまくOOで記述されたものは、非OOの世界からはdesignのように見える、
と。
(XPが「意図を表現する」ことで目指しているのもそこですね。)