らんだむな記憶

blogというものを体験してみようか!的なー

テスト駆動開発 (2)

後半読み方が雑になったので正確な感想にはならないがとりあえず備忘録的にまとめる。

雑記

結局は

  • ゴールは「動作する綺麗なコード」
  • TDD はテスト技法ではない。TDD は分析技法であり、設計技法であり、実際には開発のすべてのアクティビティを構造化する技法なのだ。

に集約されてくるのだろう。いわゆる単体テストのコードは「動作する API ドキュメント」としての側面もあると思う。とすればそこに表現されているべき内容は「API の使い方の見本です。自然に使えるでしょう?」というユーザー目線の構造であると思う。テストコードが複雑怪奇であるということは実装がお粗末ということも意味するだとう。このように、実装からテストを導く場合、そこに現れるのは、都合やその時の気分で実装されたものになんとかテストを被せて「とりあえずこうしか実装できなかった。不自然でもこう使ってくれ。」というデベロッパ目線の構造であると思う。この両者は必ずトレードオフがある。同署を見て、それぞれの章の最後に現れたコードは完璧なものではない。ユーザー目線では「ここはこういうクラスで使えないか?なんでこんなクラスが仲介に入るのか・・・」という不満が残った。だがそれは実装の都合や言語定義の制約から考えると一般には仕方のないことだ。トレードオフ関係にある 2 つの目線について、ユーザー目線でトップダウンで掘り下げ、デベロッパ目線でボトムアップで掘り起こすことのせめぎ合いの極限として “アトラクタ”(p.277;「力学系」の用語だったかな?) を模索する設計行為として TDD が位置付けられる、ということのように思われた。

第 12 章におけるメタファーは難しい。どういう瞬間にどういう神が降臨するかで、設計がどこに収束するか分かれそうだ。その多様性に、プロダクトの多様性が出ると思われる。Expression は個人的に命名するなら AbstractMoney にするかもしれない。「式」の結果として現れる「抽象的な金銭」だ。「$5 + 10 CHF = $10」なら、Expression は左辺「$5 + 10 CHF」を意味するが、個人的には右辺「$10」をクラスにしたい。

class Money implements Expression {

がどうにもシックリこない。それは仕方ない。左辺と右辺を結ぶ同値関係「=」が実装できない・・・というか後に導入される class Bank が吸収することになる。「2.999...」と「3」は無条件には一致しない。形式的な数の集合に同値関係を入れて完備化して初めて一致する。どっちがシックリくるかというのは幾分好みによるところだろう。
とりあえず、好みはあるが、同署のようにいい感じのメタファーが思いつく時点で幸運であり、実際の現場でそのような僥倖が得られないのであれば、時として残念なことにもなりえよう。そのことは p.83

  • TDD は、設計のひらめきが正しい瞬間に訪れることを保証するものではない。しかし、自信を与えてくれるテストときちんと手入れされたコードは、ひらめきへの備えであり、いざひらめいたときに、それを具現化するための備えでもある。

ということに表されている。要は保険だ。

さて、本来はどの瞬間からでも TDD に移行できるはずだが、少なくとも 2 つの問題がある。1 つは p.192「テストファースト」にある:

  • いつテストを書くべきだろうか— —それはテスト対象のコードを書く前だ。実装を終えてしまったら、きっと後からはテストを書かないだろう。プログラマの仕事は、機能を実装することだからだ。

もう 1 つは、p.273「途中から TDD に乗り換えるにはどうすればようか」にある:

  • 最大の問題は、テストのことを考えずに書かれたコードは、えてしてテストが書きにくいことだ。インタフェースが設計されていないので、ロジックを一部切り出して動かし、結果を確かめるくらいしかできない。

後者は、仮に乗り換えてもユーザーとデベロッパの両視点の収束先は TDD に切り替えた時点より良いところに到着できないということだ。それとてプロダクトの実現の多様性の 1 つであるので、そういう到着の仕方も解としては存在するが、後の負債を残すことになる。なので、もっとも瑞々しく高い理想を持った最初の時点で TDD をやるのが一番よろしいということになる。TDD を始めた瞬間にユーザー目線が発生する。

まとめ

まとめると、TDD はプロダクトの分析と設計において、仮想のユーザーを登場させ、自分というデベロッパと仮想ユーザーの間で議論を交わし、理想の落とし所を模索するという手法ということであろう。なので「テスト技法」ではないし、最初に「テスト」を書けばテストファースト → TDD になるというわけでもない。

蛇足と現実への視線

まとめの後で些かまとまりが悪いが、2 点追記したい。1 つは xUnit の話。xUnit には大変世話になっているし、模造品を作ったこともあるので、WasRun クラスの命名は気にくわないという点はあるが、あえて車輪の再発明をする意味が勉強の観点で存在することに同意する。p.183 の

  • 私が新しいプログラミング言語に触るときには、まず xUnit を実装してみる、8 個から 10 個のテストが動作するようになるころには、日々のプログラミングに必要な機能は登場していることだろう。

というのはなかなか興味深い。が、非常に正しいことだと思う。幸い、xUnit のバリアントは至る所にあるので、自分で実装するにあたっても既に道筋はつけられているので、途方にくれることはないだろう。

もう 1 つは p.200「説明的なテスト」の内容である:

  • チームの中であなたしか TDD を行っていないならば、イライラすることもあるだろう。しかし、テストが書かれたコードは結合時の問題や不具合報告が少ないこと、そして設計がシンプルで説明がしやすいことがじきにわかる。そうなったら、チームがテストを書き始め、そしてテストファーストに夢中になることも起こりうる。

これについては後半かなり思うことがあるが、次の一節が重要である:

  • TDD の強要ほど、TDD の普及を妨げるものはない。あなたが管理者やリーダーであっても、誰かの働き方を強制的に変えることはできない。

付録 C にあるように「自分たちで自動テストを書くことが「ふつう」な世の中になりました。」ということが体感できるほどの環境ではないと辛いことも少なくない。テストが破損する改変を入れられて放置されるだとか、他人は変わらないのでテストが新たに書き始められることはなく、不具合等の問題が少ない点だけが着目され仕事を増やされてしまう(但し、インセンティブは得られない)という強烈なモチベーションのダウンを誘発されることがあるからだ。

  • TDD/BDD 自身は開発方法論のトレンドの中心から離れ、継続的デリバリー、DevOps を構成する技術のひとつに収まりました。

ということで、世の中は TDD/BDD を超えて CI/CD, DevOps に行ったらしいが、イマイチその普及率もよく分からないし、そんな恵まれた状況は活発な OSS でしか見たことないので、せいぜい辛くならない程度にやっていきたい・・・。