2014年12月にRuby 2.2がリリースされる予定です1。
Ruby 2.2にはRuby 1.9.1のときに外されたtest-unitというテスティングフレームワークが再びバンドルされる予定です。Rubyのテスティングフレームワーク周りに詳しくない人にはよくわからない状況でしょう。そこで、Rubyのテスティングフレームワークの歴史を説明することで状況を整理します。
名称の整理
この説明の中ではたくさんのテスティングフレームワークが登場します。似たようなものもあるため、最初にテスティングフレームワークの名称を整理します。この説明の中で登場する名称は次の通りです。
- RubyUnit
- Lapidary
- rubyunit
- Test::Unit
- test/unit
- test-unit
- miniunit
- minitest
- RSpec
違いがわかりますか?ざっくり説明すると次の通りです。
- RubyUnit
Lapidaryと同じくらいの時期に作られたRuby用のテスティングフレームワーク。たぶん一番最初に作られたRuby用のテスティングフレームワーク。
- Lapidary
たぶん一番最初に作られたRuby用のテスティングフレームワーク。RubyUnitと同じくらいの時期に作られたRuby用のテスティングフレームワーク。
- rubyunit
- Test::Unitが提供するRubyUnit互換API。
- Test::Unit
- LapidaryとRubyUnitを統合したテスティングフレームワーク。Ruby本体に入っていた時期がある。
- test/unit
- Ruby本体に入っているtest/unit/以下のファイル。時期によって示すものが違う。(後述)
- test-unit
- Ruby本体に入ったTest::Unitが本体から分離され、gemとして開発が続いているテスティングフレームワーク。
- miniunit
- minitestの前身のテスティングフレームワーク。
- minitest
- Test::Unitに変わってRuby本体に入ったテスティングフレームワーク。
- RSpec
- Ruby本体に入ったことがないテスティングフレームワーク。
それでは、歴史を振り返りながらRubyのテスティングフレームワークを整理しましょう。
Ruby 1.6の時代
Ruby 1.6の頃はRubyはテスティングフレームワークをバンドルしていませんでした。
テスティングフレームワークが広まるきっかけになったのはエクストリーム・プログラミングです。最初のXPの本が出版されたのが1999年です。日本語版は2000年の12月です。この本の影響2でSmalltalkやJava以外でのテスティングフレームワークの実装がでてきました。もちろん、Ruby用の実装もでてきました。
RubyUnitのサイトを見ると、最初に公開された3のが2001年9月2000年11月です。ちなみに、RubyUnitの作者はRubyにバンドルされているWin32OLEの作者で、日本の人です。
同じく、Lapidaryのサイトを見ると、最初のバージョンがリリースされたのが2001年3月です。ちなみに、Lapidaryの作者は後述するTest::Unitの作者で、海外の人です。
当時、walkitというテスティングフレームワークもあったようですが、インターネット上から情報を見つけることはできませんでした。
Ruby用のテスティングフレームワークがでてくると、Ruby本体にバンドルしようという話が進みます。
日本Rubyの会の高橋代表理事の2001年10月の日記によると、Lapidaryの作者がLapidaryとRubyUnitを統合したTest::Unitを作って、それをRuby本体にバンドルすることになったそうです。ただし、Ruby 1.6の間はRuby本体にバンドルされませんでした。
ここまでのまとめです。
- RubyUnitが作られた
- しばらくしたらLapidaryも作られた
- RubyUnitとLapidaryを統合してTest::Unitになった
Ruby 1.8の時代
2003年8月にリリースされたRuby 1.8.0にTest::Unitがバンドルされました。なお、既存のRubyUnitユーザーのことも配慮して、Test::UnitはRubyUnit互換のAPIを提供していました。これがrubyunitです。ちなみに、rubyunitという表記はるりま以外では見たことはありません。
Ruby 1.8にはTest::Unitがバンドルされていました。require
するときはrequire "test/unit"
と書くことからtest/unitとも呼ばれるようになりました。
ポイント:Ruby 1.8の頃にtest/unitと言ったらTest::Unitのこと。
さて、Ruby 1.8にバンドルされたTest::Unitですが、2003年の間はそこそこ活発に開発が続いていました。しかし、それ以降はほとんど改良はありませんでした。この状況はRuby 1.9.1がリリースされる2009年1月まで変わりませんでした。
そんなTest::Unitがのんびり暮らしていた2005年にRSpecの最初のバージョンがリリースされました。RSpecの開発は活発で便利な機能が追加されていきます。RSpecの記法がよいと思う人と記法に抵抗がない人は、便利なのでTest::UnitではなくRSpecを使うようになっていきました。
Test::Unitの開発が停滞していることに不満を感じている人が2人いました。後のminitestの作者と、後のtest-unitの開発者です。
minitestの作者はTest::Unitのメンテナーになりました。メンテナーになったminitestの作者は、Test::Unitは複雑すぎて自分はメンテナンスできないと主張しました。
ここまでのまとめです。
- Test::UnitはRuby 1.8.0にバンドルされた(2003年8月)
- Test::UnitはRubyUnit互換API(rubyunit)を提供していた
- RubyにバンドルされてからTest::Unitの開発は停滞していた
- RSpecが現れた
- Test::Unitのメンテナーが、後のminitestの作者に変わった
- 後のminitestの作者は複雑すぎてTest::Unitをメンテナスできないと主張した
Ruby 1.9の時代
2007年12月にリリースされたRuby 1.9.0で、Test::Unitが提供しているRubyUnit互換APIは削除されました。
2009年1月にリリースされたRuby 1.9.1ではTest::Unit自体もRuby本体から外れました。何があったのでしょうか。
後のminitestの作者は複雑すぎてTest::Unitをメンテナンスできないため、もっと小さなテスティングフレームワークにしなければいけないと考えました。その考えのもと作ったのがminiunitです。miniunitはRuby 1.9.0に入りそうになりましたが、後のtest-unitの開発者が反対したためRuby 1.9.0には入らずRuby 1.9.1でRuby本体に入りました。この間にminiunitからminitestに名前が変わっています。
minitestがRuby本体にバンドルされたタイミングでTest::UnitはRuby本体から外れました。Ruby本体から外れたTest::Unitはtest-unitというgemになります。test-unitというgemはこれをベースに今でも改良を続けています。
Test::UnitはRuby本体から外れましたが、Test::Unit互換APIは残りました。なぜならRuby本体のテストはTest::UnitのAPIで書かれていたからです。テスティングフレームワークが変わったからといって既存のテストを書き直さないといけないのは受け入れられなかったのです。
このminitestの上に実装されたTest::Unit互換APIをtest/unitと呼んでいます。
ポイント:Ruby 1.9の頃にtest/unitと言ったらminitestの上に実装されたTest::Unit互換APIのこと。Test::Unitのことではない。
ここまでのまとめです。
- 後のminitestの作者はメンテナンスできる小さなテスティングフレームワークとしてminiunitを作り始めた
- miniunitはminitestに名前を変えた
- Test::UnitはRuby 1.9.0までバンドルされていた(2007年12月)
- Test::UnitはRuby 1.9.1で外れた(2009年1月)
- Ruby 1.9.1でminitestがバンドルされた
- Ruby 1.9.1でminitestの上にTest::Unit互換APIを実装した(test/unit)
- Test::Unitはtest-unit gemとして開発を継続している
Ruby 2.0時代
2013年2月にRuby 2.0.0がリリースされました。テスティングフレームワークについて特筆すべきことはありません。minitestもtest-unitもRSpecもどれも停滞することなく開発が続いています。
Ruby 2.1時代
2013年12月にRuby 2.1.0がリリースされました。2014年11月現在の最新の安定版です。
test/unit(minitestの上に実装したTest::Unit互換API)に陰りが見えてきました。minitestがminitest 5.0.0で後方互換性のないAPIの変更を導入したのです。
この非互換の変更をうけてtest/unitはメンテナンスできなくなりました。Ruby開発チームとしては既存のテストを動かすためにtest/unitは必要です。しかし、最新のminitestでは動きません。つまり、Ruby本体に最新版のminitestをバンドルできなくなったということです。
この状況に対応するため、Ruby本体のテストはminitest 4とその上に実装されたtest/unitで動かすことにしました。ただし、このminitest 4とtest/unitはRubyユーザー向けのものではなく、Ruby開発チームだけが使うものとしました。minitestとtest/unitはRubyのソースの中ではlib/minitest/とlib/test/unit*に置かれていましたが、それをtest/lib/以下に移動しました。
移動前:
lib/minitest/*.rb
lib/test/unit.rb
lib/test/unit/**/*.rb
移動後:
test/lib/minitest/*.rb
test/lib/test/unit.rb
test/lib/test/unit/**/*.rb
これで、最新版のminitestをバンドルしてもRuby本体のテストはそのまま動き続けるようになりました。
ポイント:Ruby開発チームの中でtest/unitというとtest/lib/以下にあるtest/unitのことを指す。
ここまでのまとめです。
- minitestはバージョン5.0.0で後方互換性のないAPI変更を入れた
- test/unitはminitest 5.0.0をサポートできなくなった
- Ruby本体はminitest 4.7.5とtest/unitをtest/lib/以下に移動してRuby本体のテストではそれら古いバージョンを使うことにした
- test/lib/以下にコピーしたminitestとtest/unitをRuby開発チームがメンテナンスしていく
- Ruby開発チームがtest/unitと言ったらtest/lib/以下にあるtest/unitのこと
Ruby 2.2時代
Ruby本体のテスト用のminitestとtest/unitをtest/lib/以下に動かすことにより最新のminitestをバンドルできるようになりました。
しかし、このままRuby 2.2.0をリリースするとTest::Unit互換APIがなくなってしまいます。さらに、minitestはAPIの互換性がなくなっているので既存のテストはそのままでは動きません。つまり、既存のRubyが提供しているminitestまたはtest/unitを使っているユーザーはRuby 2.2.0にアップグレードするとテストが動かなくなるということです。
それだとあんまりだということで、Test::Unit互換APIを提供するためにRuby 2.2.0にはminitestだけでなくtest-unit(gemとなって開発が続いていたTest::Unit)がバンドルされることになりました。
なお、Ruby開発チームにはRSpecをバンドルするという選択肢はありませんでした。
ポイント:Rubyユーザーがtest/unitというとtest-unitのことを指すことになるかもしれない。
ここまでのまとめです。
- Ruby 2.2.0には最新のminitestがバンドルされる予定
- Ruby 2.2.0には最新のtest-unitもバンドルされる予定
- Rubyユーザーがtest/unitと言ったらtest-unitのことになるかもしれない
まとめ
Ruby 2.2.0にどうしてtest-unitが再バンドルされるのかを、Rubyのテスティングフレームワークの歴史をたどりながら説明しました。10年以上前のサイトが今でもアクセスできると昔のことを確認するときにとても便利ですね。
Ruby 1.9.1でTest::UnitがRuby本体から外れた後、Test::Unitはtest-unit gemとして開発が続いてきました。Ruby 2.2.0で再バンドルされるのを機にtest-unitも触ってみてはいかがでしょうか?Test::Unitの頃しか知らない人はかなり便利になっていることに驚くでしょう。
最近のtest-unit関連情報:
- RSpec をやめて Test::Unit に戻る - @tmtms のメモ
- Ruby - Test::Unitでテストを書く - Qiita
- Ruby用単体テストフレームワークtest-unitでのデータ駆動テストの紹介 - ククログ(2013-01-23)