Rails 3.x 時代のテストフレームワーク

2010/04/24

Rails 1.x の頃、テストと言えば Test::Unit であり、Fixtures でした。

この2つがあったからこそ、私は Rails を好きになったんだと言えます。

Test::Unit は Ruby 標準ライブラリの1つですが、Rails はそれを巧妙に拡張して、自らと一体化させていました。

Rails は Web アプリケーションを開発するためのフレームワークであり、同時にその Web アプリケーションをテストするためのフレームワークでもあったわけです。

Fixtures は、テストの対象となるサンプルデータをデータベースに投入するためのツールです。

テストを開始する時点でのデータベースの状態を YAML 形式あるいは CSV 形式で記述しておくと、Fixtures はテストを行う直前にデータベースをその状態に戻してくれます。つまり、Fixtures によって再現性のあるやり方でアプリケーションのテストを実施できるようになるのです。

かつて「P」で始まる他のプログラミング言語で Web アプリケーションの開発をしていた頃、私がこの種の開発手法を明確に望んでいたかどうかはよく覚えていません。とにかく、Rails に出会った当時の私は、それらを新鮮に感じ、強く惹かれました。

無論、Test::Unit や Fixtures がもたらすテスト環境は、Rails が初めて可能にしたものではありません。しかし、Rails はそれらの仕組みを一体化させていたため、非常に分かりやすかった、という点が非常に重要です。

当時、私は数人のプログラマーを部下に持つ管理職でもあったので、何か新しいものに取り組み時には、説明のしやすさを常に気にしていました。

さて、時は流れて、Rails 3 のリリース前夜になりました。

その間に Rails アプリケーションのテストに関して起きたことは、次の3点に要約されます:

  1. Test::Unit と Fixtures は少しずつ改良されつつ Rails 標準の地位を保っている。
  2. ビヘイビア駆動開発(BDD)が提唱され、そのためのフレームワークとして RSpec が登場した。
  3. Fixtures の使いにくさが強く認識されるようになり、複数の代替候補が登場した。

変化は2007年から2009年にかけて徐々に進みました。

RSpec に対する Rubyist たちの初期の反応は、2008年3月に書かれた記事 スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Rails (モデル編) において典型的に見ることができます。

この記事の著者(かくたに氏、もろはし氏)は、Test::Unit で次のように記述していたものが

 def test_empty?
   assert(@empty_array.empty?)
 end

RSpec では

 it "should be empty" do
   @empty_array.should be_empty
 end

のように記述できる、ことをやや興奮気味に伝えています。

その当時、私は RSpec のことを知って、同僚に否定的な感想を述べたことを覚えています。「少なくとも日本では流行らないんじゃない」と私は感じました。「It should be empty.」といった英語の文は、読むだけならまだしも、日常的に書くのは結構つらい。まして、部下のプログラマーにそれを書かせることなど、気分が滅入るような話だ、と(今振り返ると)考えたんだと思います。

というわけで、しばらくの間、私は様子見を決め込んで、Test::Unit と Fixtures による伝統的なテスト駆動開発(TDD)を続けました。

しかし、The Pragmatic Bookshelf から The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends が出版されることが決まったところで、私はその PDF 版を購入して、急いで時代を追いかける決心をしました。その頃は、もう独立して今の会社を作った後だったので、あまり周囲のことを気にする必要もありませんでした。早速、進行中のプロジェクトに RSpec と BDD を適用してみたところ、幾つかの点では非常に気に入りました。

以前よりも非効率になったと感じた面もありましたが、それは多分、私がまだ RSpec を正しく理解していないからだろうと、その時は思っていて、知人にも RSpec を勧めたりしていました。

しかし、今、私の気持ちは揺らいでいます。

弊社(株式会社オイアクス)は時々 Rails に関するセミナーを開催しているのですが、以前からテスト駆動開発についてやってほしいという要望が寄せられていて、テキストを Rails 3 向けに書き換えるついでに、RSpec のこともカリキュラムに加えることを検討していました。そのため Google の力を借りていろいろと調べたのですが、その中で RSpec についての否定的な意見を幾つか読みました。

どの記事にも共通する点は、Shoulda との比較を行っていることです。

Shoulda は、Test::Unit を置き換えるフレームワークではなく、Test::Unit を拡張するためのパッケージです。

記事の筆者たちが強調しているのは、RSpec で本当にすばらしいのは context という概念を導入したことで、それは Shoulda にも備わっている、ということです。

また、Dave Thomas は「@empty_array.should be_empty」や「add.a.diary.entry.for("Lunch").for(August.10.at(3.pm))」みたいな自然言語風の書き方を要求するフレームワークは、readability を高めるかもしれないが、writability を失わせ、不確かさや曖昧さを増すだろうと、述べています。

さらに私が付け加えるなら、既存のアプリケーションで Test::Unit から RSpec へ移行するためにはテストを大幅に書き換えなければならないけど、Shoulda を導入した場合にはその必要がない、という点を指摘できるでしょう。

では、私が RSpec を捨てて Shoulda に向かうかというと、そう単純な話ではありません。

いま強く思うのは、Rails アプリケーションのテストをめぐって、ある種の覇権争い(比喩的な意味です)が起きていて、当面決着が付きそうもない、ということです。そういう状況には良い面もありますが、Rails 開発者の裾野を広げるためにはマイナスかもしれません(分かりにくいから)。

Fixtures の代替候補についても同じような状況があります。Factory GirlMachinist という非常によく似た有力なパッケージが並立しています。

さらに TDD/BDD を深く知ろうとすれば、Mocha, Cucumber, Webrat/Capybara, Rcov などの様々なパッケージについても知る必要があるでしょう。

少なくとも現在は、Rails のテストを取り巻く状況が複雑に拡散しつつあります。やがてデファクトスタンダードが現れてくるのか、長期に渡って落ち着かないのか、しばらく見守りたいと思います。
--
黒田