最近Web上でも仕事場でも、RSpecをやめて別のテストフレームワークに変えようと思っている……みたいな話をちょくちょく見聞きするようになった。僕がRuby on Railsで開発を始めた2012年8月当時、すでにRSpecはテストフレームワークのデファクトと言ってよかった。一斉を風靡したRSpecが、なぜ今見直され始めているのか。
きっかけになったのは今年4月の、Rails作者であるDavid Heinemeier Hansson(以下DHH)によるTDD is dead発言だと思う。
5月にはこの発言によるTDDへの風評被害を重く見たKent Beck*1が、レフリーにMartin Fowler*2を迎え、DHHと相対するドリームマッチが開催された。この会談の内容は、POSTDで全文日本語翻訳されている。
【翻訳】テスト駆動開発(TDD)はもう終わっているのか?PART I | POSTD
上記会談ではDHHの「TDD is dead」発言の真意や、Kent BeckによるTDDが生まれた歴史的経緯が語られており、図らずもTDDのこれまでを俯瞰できる内容になっている。この会談の内容を元に、まずはTDDについて整理してみたい。
TDDとは何か?
記事中でMartin Fowlerは「セルフテスティングコードとTDDは違う」と指摘している。以下のような意味だと思われる。
TDD(テスト駆動開発)
テストコードを書いてから、それにパスするように実装コードを書く開発ワークフロー(テストファースト)
TDDをさらに押し進めた概念でBDD(ビヘイビア駆動開発)という考え方もある。話を単純にする為、特に断りが無い限り文中ではTDDはBDDを含む概念として話を進める。
セルフテスティングコード
テストコードそのもの
TDDは机上の空論なのか
DHHは開発者がカスタマーサービスに関わっていないことを例に出し、Webアプリケーションのようなプロダクトでは要求仕様は非プログラマーのステークホルダーしかわからないため、プログラマーによるセルフテストには限界があること指摘している。実際の業務を知らない人が正しい仕様を作れるわけが無いという、至極真っ当な意見だと思う。
また、QA(品質保証)について以下のように語っている。
TDDがプログラマに「過剰な自信を抱かせ、QAは必要ないと思い込ませてしまった」と考えています。QAという古いモデルは崩壊し、代わりにTDDが席巻しました。Davidは「自分以外の誰かにテストさせない限りは、適切な品質を確保し、優れたソフトウェアを作成することはできないと思う」と述べ、自分はQAの担当者を迎え入れることがどれだけ有益なことか知っているため、今の状況を残念に思うと言いました。
DHHはTDDがプログラマを傲慢にし、全てを決めきれるという勘違いをさせてしまったと思っている。
まとめ役?のMartin FowlerはTDDの非現実性を以下のように表現している。
実際には、それらは非技術系のステークホルダー(利害関係者)と一緒にテストを書くという、架空の場所でしか役に立ちません。
彼らの言うように、TDDは机上の空論なんだろうか?
TDDが有効なケース
個人的にTDDがうまくいったという経験がある。 以前、9路盤の囲連星(囲碁と七目並べの間の子みたいなゲーム)ができるWebサービスを作った際に、「現在の盤面の状態」と「新しい一手」を入力として与えて、結果の盤面状態を返すルールクラスを実装することになった。
まず、ルールクラスの単体テストケースを僕がJUnitテストケースとして実装した。下記はその時に作った実際のテストケースだ。
ルールクラスの実装担当者には、このケースを通るようにRuleUtilクラスを実装してくれと依頼した。単体テストケースによりルールの正しさが担保されていたので、画面と結合してもおかしな挙動になることはなかった。
上記ケースは以下のような特徴があったためTDDを実施しやすかった。
- モジュールの独立性が高い(データベース、HTTPなどに依存しない純粋な処理ロジック)
- 囲連星という、完成された仕様がある
囲連星のルールのように、すでに仕様が確定していることに関してはTDDを実施できるし、有効だと思う。何を当たり前のことを……と思うかもしれないが、考えてみてほしい。実際の開発案件で実装スタート時点で仕様が確定している場合のほうが少なくない?
TDDは「Webアプリケーションに」向いていない
Martin Fowlerによれば、DHHはあらゆるTDDを否定しているわけではない。
TDDを通じてテストを学んだ彼は、TDDをとても気に入り、全てのものでそれを試してみようとしました。しかし、MVCモデルのWebアプリケーションの多くの領域では、TDDが適していないと徐々に気づいたと言います。これは、ある状況においてTDDが役に立たないということを意味しているわけではありません。Davidの場合は、TDDが役立つケースが少なかったというだけです。
若干、Martin Fowler自身による解釈が入った発言にも思える。 とにかくDHHは、RailsでWebアプリケーション開発する場合には、要求仕様の性質やシステムアーキテクチャの両面からTDDは難しいと考えているらしい。
TDD実践ツールとしてのRSpec
ところで、DHHほどのフログラマが現実的じゃないと考えるRailsでのTDDを一般の開発者達はどうやって実践してきたのだろうか?
そう、RSpecを使ってだ。 RSpecの設計思想は「Rubyist Magazine」のRSpec解説記事を読むとわかる。
では、なぜ Test::Unit ではなく、RSpec を選ぶのでしょうか。冒頭の FAQ を再び。 「RSpec って、要は Test::Unit でやっていることを別の書き方にしただけでは?」 ――確かにその通りなのですが、その「書き方」が重要だというのが私たちの思いでした。
RSpec が「振舞定義用の DSL」を提供して、プログラマに Test::Unit とは異なる 書き方をさせているのは、ある考え方を私たちに伝えるためです。その考え方とは、 テスト駆動開発 (Test Driven Development:TDD) です。
なぜ、RSpec なのか | スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Rails (モデル編)
TDDでは、よく「実装をテストするな」と言われる。Java界隈なんかでもテストコードはインターフェースに対して書かれるべきと教えられる。 Javaのインターフェースのようなコード表現の仕組みがないRubyにおいて、RSpecのようなものが進化してきたのは興味深い。普通にテストコードを書いているだけだと仕様を意識できないから、意識できる文法を作ろうという訳だ。こういう発想がでてくるのがRuby界隈の面白さだと思う。
というわけで、TDDに特化したRSpecを使えば盆百のプログラマでもTDDができそうだ。本当に?
TDDの希薄化とRSpecの進化
TDDは、Martin FowlerがAgileに対して言うところの「semantic diffusion(意味の希薄化)」に陥っていたように思える。つまりTDDテストツールを使ってさえいれば、それはTDDだという……。
TDDの本質的な意味は「実装前に仕様を決めよう」ってことだ。しかし、DHHによればWebアプリケーションではTDDの実践に無理があった。この現実とのギャップはRSpecの進化の方向性にも影響を及ぼしたのではないか。
RSpecはTDD本来の「実装前に仕様を決めよう」という本質的な部分ではなく、記述フォーマットを自然にするという形式重視の方向に進化していった*3。例えば以下のように。
Ruby - RSpecのshouldはもう古い!新しい記法expectを使おう! - Qiita
RSpecが、書き方による意識の変革を指向しているのはわかった。 でも例えばshouldをexpectにして、何の意識を変革すればいいのか?理由はあったと思うが、多くの開発者にそれは伝わらなかったンじゃないか。
RSpec をやめて Test::Unit に戻る - @tmtms のメモ
私がMinitestとRailsのFixturesにハマった7つの理由 - 有頂天Ruby
最近のRSpecやめてみようかな……の流れは、単に流行に陰りが見えてきただけかもしれないが、WebアプリケーションにおけるTDDの実践というテーマ自体に内在した歪みが現れてきているようにも見える。
我々はそもそもTDDしていたのか?
僕がいままで経験したRSpec採用プロジェクトで、原理原則通りのTDDをしていたプロジェクトはゼロだ(単に僕の仕事場の意識が低いだけという可能性はある)。結局、みんな実装してからRSpecでテストを書いていた。
TDDの実践は難しいけどとりあえずRSpecだけは使っておくことで、いつかちゃんとTDDしようみたいな感覚で問題の先送りをしていた。そして、RSpecの提供する記述の美しさという、(プロダクト視点で見たら)本質的な価値でない部分の快楽に溺れていた気がする。
DHHの「TDD is daed」はそういう開発者に対して、そんなにTDD辛いならやめちゃえよっていう助け舟のつもりだったんじゃないか。記述の美しさやリファクタリングに感じる価値は、麻薬のようなものだと気づかせてくれたようにも思える。DHHがRails標準のテストフレームワークに、RSpecはねーよって言っていた意味が今ならわかる。
最近、RSpecをTest::Unitやminitestに置き換えるエントリを目にするけど、「TDDを諦めます」という言及が無いところに違和感がある。ひょっとして僕の職場以外はみんなちゃんとRSpecでTDDしていて、Test::Unitやminitestに置き換えるけどTDDは続けるつもりなんだろうか?もしかして、まだ記述方法みたいな部分にこだわってらっしゃるのでは。
僕の場合はもともとTDDしてなかったので、素直にあきらめて、身の丈に会ったテストフレームワークを使っていこうと思う。もちろん既存のRSpecコードをTest::Unitに書き換えるなんてことはしない。そういうことは要求仕様を満たす為の本質的なプロセスとは関係がないからだ。
最後に、Martin Fowler先生の名言をひとつ。
ソフトウェアの開発に関わるのであれば、それについて思慮深く考え、技術をやみくもに選ばずに、自分やチームにとって何が機能するのかという判断に基づいてワークスペースを構築しなければならない
さすが、Martin Fowler先生やで〜。