RSpec 3に向けての計画(日本語訳)
Myron Marston » The Plan for RSpec 3の微妙訳です。(翻訳最中なう)だいたい翻訳しました。訳がうんこなのは勘弁(ご指摘いただけると助かります)。
2013/7/23 21:25 id:kakutani さんのツッコミをもとに、誤訳等を修正しました。ありがとうございます(〃・ิ‿・ิ)ゞ
RSpec 3に向けての計画
RSpec 2.0は2010年10月にリリースされました。 リリースされてから今までの3年間、後方互換性を保ったままRSpecを継続的に改善してきました。 しかし、RSpecの2.xより古いリリースとの後方互換性を保つために残しているひどいコードの蓄積は限界点に達しています。 RSpec 2.14はRSpec 2の最後のリリースになるでしょう(今後も多分bugfixのリリースすることはあるでしょう)。
我々はRSpec 3に取り掛かっています、私は、私たちがRSpecをどのような方向に進めていくかについて、考えを共有したいと思います。
もちろん、ここに書かれているものの中に、既に確定していて絶対に変えらないようなトピックはありません。 結局のところ皆さんが使ってくれていることがRSpecプロジェクトが成功している理由です。 我々がRSpec 3で取ろうとする方向について、あなたに何か考えがあるのなら、ぜひ発言してください!
何が取り除かれるか
Ruby 1.8.6と1.9.1のサポートが終了します
RSpec 2.xではMRIチームがサポートを終了してからも、長い間Ruby 1.8.6のサポートを続けてきました。 Rubyのテストインフラのエコシステムにおいて我々が重要だと感じていることは、gemの作者がいつ古いRubyのサポートをやめるか決められること、 そして、gem作者が使用することを選択したテストフレームワークが彼らがサポートするRubyのバージョンをサポートしなくなることによって彼らがgemのサポート終了を早めるのを強制されないことです。 Ruby 1.8.6と1.9.1はここ2年間Travisで利用できなくなっており、またCIサーバーのセイフティネットなしでは、我々がこれらのバージョン上でビルドを実行し、古いバージョンをサポートすることは非常に難しくなっています。
実際には我々はここ2年間これらのRubyバージョンを「セミサポート」しています。 もしもユーザーがこれらのRubyバージョンについての問題を報告した際には、修正はしますが、それ以上のサポートはしません。
それに、これらのバージョンのサポートを終了する時期に来ています。 我々はRSpec 3では1.8.7と1.9.2、それより新しいRubyのバージョンについて、継続的にサポートする計画です。 1.8.7が既にレガシーであることを考えると、我々はおそらくRSpec 4で1.8.7のサポートを終了するでしょう。 とはいえ、もしもTravisがそれ以前に1.8.7のサポートを終了した場合は、我々に出来るのは1.8.6と同様な「セミサポート」のみです。
Core: its
は外部gemに移動します
前にこれについて書いたので、ここでは長々と書きません。
我々はits
をrspec-coreから、外部のgemへと移動させようと計画しています。
Expectations: have(x).items
マッチャ―が外部gemに移動します
RSpecはCucumber/Gherkinが生まれる前に作られました、そして早期の目標の1つが、プロジェクトのステークホルダーが理解出来るような自然言語のように表現できることでした。
これらの初期の日々では、team.should have(9).players
のような表現がプロジェクトの目標を示していました。
その後、ステークホルダーにフォーカスしたテストとしてCucumber/Gherkinが現れ、今日ではRSpecがステークホルダー向けのテストを書く目的で使われることは珍しくなりました。
have(x).items
として知られるマッチャ―(have_at_least(x).items
やhave_at_most(x).items
のような兄弟マッチャも含む)は、expect(team.players.size).to eq(9)
のようにシンプルでうまく動く表現に比べると、必要以上に複雑です。
我々はこれらのマッチャをrspec-expectations
から、外部のgemに移動させようと計画しています。
Core: 明示的なデバッガーのサポートをやめます
RSpecは長い間ruby-debug
gemによるデバッガーを有効にするための-d
/--debug
コマンドラインオプションをサポートしてきました。
しかし、今日ではruby-debug
はRubyをデバッグするのに使うただ1つのgem(あるいはメインのgem)というわけではありません。
debuggerはMRI 1.9.2+でデファクトスタンダードになっています、そして多くの開発者がpryをデバッグに好んで使うようになっています。他のRubyインタプリタ、たとえばRubniusは自身にデバッガの機能を持っています。
我々はRSpec 3で明示的なデバッガーのサポートをやめ、取り除こうと計画しています。
コマンドラインオプションを取り除くのに加えて、我々はruby-debug
がロードされていない際にKernerlにモンキーパッチするのをやめます。
そんでもって、debuggerがロードされてないのにdebuggerと書いているとNoMethodError
が飛ぶようになるでしょう。
もしあなたが引き続きコマンドラインオプションを使ってdebuggerをロードしたいなら、require flag(-r
)が使えます、-rdebugger
のように使います。
Core: rcovとの統合をやめます
RSpec::Core::RakeTask
は長い間rcov向けのオプションを持っていました。
RCovはMRI 1.8でのみ動作し、最近のRuby開発者のほとんどはコードカバレッジが必要なときはSimpleCovを使うようになっています。
SimpleCovは非常にシンプルで、RSpec自身からの明示的なサポートなしで、RSpec(あるいは他のフレームワーク)と一緒にうまく動きます。
Core: Autotestとの統合は外部のgemに移動します
Autotestは主に継続的にRubyのテストを実行するために使われています。 今日では、guardがより人気になったので、RSpecがAutoTestとの統合をrspec-coreの中で行う理由はありません。
Core: TextMateフォーマッターがTextMateのbundleに移動する
長い年月、TextMateはRuby開発者が使うもっとも人気のあるエディタでした。
RSpecはTextMate向けのformatterを長い年月もっていましたが、今日ではTextMateはRubyの開発者の間で人気のエディタだとは言えなくなっており、TextMateフォーマッターをrspec-core
にいれておく説得力のある理由がありません。
大量のDeprecations
RSpec 2.14はdeprecatedになって2年以上たつようなものを大量に含んでいました。 我々はdeprecatedになったほぼすべてのAPIを取り除く計画です。
古いexpectation/mock記法についてはどうなるの?
RSpec 2.11では新しいexpect
ベースの記法がrspec-expectations
に入りました。
RSpec 2.14では、我々はrspec-mock
をアップデートし、似たような記法を使うようにしました。
新しい記法を紹介してから、私は「どれぐらい近いうちにshould
ベースの古い記法をdeprecatingにするの?/とりのぞくの?」という質問を受けました。
私は「絶対に取り除くことはない」とは言いませんが(未来のことは誰にもわかりません), 今のところ我々に古い記法を取り除く計画はありません。
ユーザーは長年にわたり、古い記法でコードを書いてきました。私たちは(新規プロジェクトなら尚のこと)新しい記法を使うことを推奨していますが、かといって今すぐ古い記法を取り除いてしまうことはユーザーにとって非常に不親切です。それに、古い記法のサポートのメンテナンスはものすごく大変というわけでもありません。
RSpec 3では、我々は古い記法をデフォルトで無効にし、ユーザーがオプトインでそれを使うよう強制することを検討しました。 しかし、私はそうするのは新しくRSpecを使うユーザーにとって、不親切になると考えました。
初めてRSpecを試してみる人たちにとっては、チュートリアルからコピーしてきたコードでNoMethodError
がでるのは非常にがっかりすることになるでしょう。
経験豊かな人はかんたんに古い記法を無効にできるのに対して、新人がチュートリアルにある古い記法を有効にして使うためのRSpecの知識を十分に持っているとは言いがたいです。
とは言うものの、我々は人々が新しい記法にスイッチするのを奨めたいので、我々はRSpec 3で、should
記法を明示的に有効にせずに古い記法のメソッド(should
, should_not
, should_receive
, etc...)を使うと警告メッセージを出すように計画しています。
これは、新規ユーザーへの優しさを保ちながら、新しい記法に向かって人々を前進させ、RSpec 4で古い記法を無効にするのをデフォルトにするための道を開くでしょう。
新機能について
ゼロモンキーパッチモード!
歴史的に、RSpecは読みやすい記法を作るため、広範囲にわたってモンキーパッチを行って来ました、describe
やshared_examples_for
やshared_context
やshould
やshould_not
やshould_receive
やshould_not_receive
、そして全てのオブジェクトで使えるstub
などです。
2.x系のリリースの最後のいくつかで、我々はRSpecによるモンキーパッチングの量を減らすように働いてきました:
- rspec-core 2.11では、
describe
はすべてのオブジェクトに追加されなくなりました、代わりに、トップレベルのmain
オブジェクトとModule
に追加されるようになりました(つまり全てのclassとmodule内で使用可能です)。 rspec-expectation 2.11では、我々は
expect
記法を追加しshould
記法を無効にするための設定オプションを提供しました 。これはshould
とshould_not
を全てのオブジェクトから取り除くものです。rspec-core 2.12では、
shared_examples_for
とshared_context
は、全てのオブジェクトに追加されなくなりました。そしてdescribe
のようにそれらはトップレベルのmain
オブジェクトとModule
にのみ追加されるようになりました。rspec-mocks 2.14では、我々は
rspec-mocks
がexpectベースの記法をサポートするようアップデートしました、そしてmockするための古い記法を無効にするオプションを提供しました。これはshould_receive
とshould_not_receive
とstub
をすべてのオブジェクトから取り除くものです。
上述したように、3.0で我々はRSpecのモンキーパッチをあてたKernel#debugger
を削除します。
また、我々はmain
オブジェクトとModule
のトップレベルDSLメソッド(describe
、shared_examples_for
、などなど)を取り除く設定オプションを提供することを計画しており、代わりにこれらのメソッドを呼ぶときにRSpec.
のプレフィックスをつけることを求めます
RSpec.describe MyClass do # exampleグループの中ではまだ`describe`を使用することが可能です: describe "#some_method" do end # `shared_examples_for`を使うことも可能です shared_examples_for "something" do end end RSpec.shared_examples_for "some behavior" do end
最終的には3つの設定オプションにより決まります(1つはrspec-expectations
、1つはrspec-mocks
、そして1つはrspec-core
)、それはRSpecにゼロモンキーパッチモードを提供します(我々はこれら3つを一括して設定出来るオプションを提供する予定です)。
我々はこれら3つのオプションをRSpec 4.0でデフォルトにする予定で、RSpec 4.0は何もしなくてもゼロモンキーパッチモードで使えるようになります。
Mocks: テストダブルのインターフェースの検証
残念ながら、テストダブルが置き換えているインターフェースと実際のインターフェースはかんたんに噛み合わなくなります。
たとえばあなたがメソッドをリネームしたり、期待される引数の数をかえたとき、変更したクラスのテストダブルを更新するのはかんたんに忘れがちです。
私は長い間、この問題を解決するrspec-fireのファンでした。
そこで私はrspec-mocks
にそれをポートすることを計画しています。
詳しくは、githubのissueでのディスカッションを見て下さい。(これらの機能のAPIとセマンティックはまだ固まっていません、githubのチケット上でぜひあなたの考えを聞かせてください!)
Expectations: 完全に組み合わせ可能なマッチャ―
RSpec 2.13では、我々はincludeマッチャーがマッチャ―の配列を受け入れるようなサポートを追加しました。 このような組み合わせ方はとても使えるので、我々はこれをRSpec3のすべてのマッチャ―で出来るようにしようと計画しています。 例えばあなたはこんな感じの構文を使うことができます:
expect { |b| some_object.do_something(&b) }.to yield_with_args(include(match(/foo/), match(/bar/)))
何を期待するかを詳細に表現しています: 「私はsome_objectがdo_somethingしたとき/foo/と/bar/にマッチする文字列を含んだ配列をyieldすることを期待する。」
我々はまた、このスタイルでマッチャ―を組み合わせたときにより読みやすいように、マッチャ―のエイリアスを追加することを検討しています。 そんで、あなたはこのように書けるでしょう:
expect { |b| some_object.do_something(&b) }.to yield_with_args(a_collection_including(a_string_matching(/foo/), a_string_matching(/bar/)))
より詳細な情報についてはGitHubのissueを見てください。
Make all matchers composable for 3.0 · Issue #280 · rspec/rspec-expectations
Core: FormatterのAPI改善
formatterにテストの状態を通知するための現在のAPIは、新しく通知を追加したり、既存の通知を変更する際に、ちょっとばかり柔軟性に欠けることが分かっています。
我々はこれを2つの方法で変えようとしています:
- formatterにすべての通知メソッドを実装することを要求するよりも、formatterが特定の通知だけをサブスクライブする方法。これであなたはformatterで必要なほんのちょっとの通知メソッドだけ実装すればよくなります、そして既にあるformatterを壊す心配なしに、新しい通知を追加することが出来るようになります。
- 通知の引数は順番にならんだ引数列から、1つの値オブジェクトに変わります。 メソッドのシグネチャを変更せずに、追加のデータを特定の通知に加えることが可能になります。
これらの変更により2.xのリリースで出来なかったさらなる改善が出来るようになります。 我々は古いAPIで書かれたformatterをラップして互換性をもたせる層をRSpec3で提供する予定なので、ユーザーが古いformatterを使っていても、かんたんにアップグレードすることが出来ます。
詳細についてはGitHubのissueを見て下さい。
Core: DSLメソッドがexampleをyieldするようになる
RSpec 2では現在実行中のexampleをexample
で見ることが出来ます。
これはexampleのメタデータにアクセスするために使うことができます。
これはときどき、ユーザーがうっかり独自のexample
メソッドを定義した際に、問題を引き起こします。
RSpec 3ではexample
メソッドを削除し、DSLメソッドにyieldでexampleを渡します:
describe MyClass do before(:each) { |example| } subject { |ex| } let(:user) { |ex| User.find(ex.metadata[:user_id]) } # ※before(:all) はexampleをyieldしません it "現在のexampleにブロックローカル変数でアクセス出来る" do |example| # do something with `example` end end
我々は、この変更によりexample APIを使うgem(例えばCapybaraのこと)に頼っているユーザーが、アップグレードに頭を抱えることを承知しています。 我々はアップグレードをスムーズにする方法について、gemの作者とともに議論しています。 詳しくはGitHub issueをみてください。
Expectations: マッチャ―のプロトコルと、カスタムマッチャ―APIの変更
RSpecはshouldベースの記法からやり方を変えていってますが、その一方でマッチャープロトコルとカスタムマッチャーのAPIはその変更に追従していませんでした。
マッチャ―のプロトコルは未だにfailure_message_for_should
とfailure_message_for_should_not
に依存しており、カスタムマッチャ―APIはmatch_for_should
とmatch_for_should_not
のようなメソッドに依存しています。
RSpec 3では、マッチャ―プロトコルとカスタムマッチャ―APIを変更し、should
が使われなくなるまでの間、既存のマッチャ―がうまく動くよう後方互換性のレイヤーを残しておき、RSpec4でそれらの互換性を取り除こうと計画しています。
我々は新しいAPIがどうなるべきか未だ確証を得ていません。もし考えがあったら、GitHub issueで知らせて下さい。
Mocks: any_instance
がブロック実装にreceiverをyieldするようになる
any_instance
を使ってメソッドをスタブするとき、通常のメソッドスタブと同様に、実装をブロックで渡すことが出来ます。
一方、あなたがブロックの中からreceiverにアクセスしたいとき(つまりインスタンスからメッセージを受け取りたいとき)、それをする方法はありません。
RSpec 3では、これらを直し、receiverをブロックの第一引数として渡します:
allow_any_instance_of(User).to receive(:age) do |user| ((Date.today - user.birthdate) / 365).floor end
後方互換性のために、我々はこの挙動を無効にする設定オプションを追加する予定です。
アップグレードのみちすじ
RSpec 3.0は、2010年以降はじめて意図的に破壊的な変更を加えることが出来るメジャーリリースとなるにも関わらず、既存のテストスイートが可能な限り楽にアップグレード出来るようなアップグレードのみちすじをたてることが我々にとって重要です。
移行を終わらせるために、我々は純粋にユーザーのアップグレードを助けるための2.99をリリースすることを計画しています。
これが我々が思い描いていることです:
- RSpec 2.99はRSpec 2.14に3.0で取り除かれることについていくつかのdeprecation warningを加えたものになります。
- RSpec 2.xで書かれた既存のテストコード一式は何の変更も加えずに2.99にアップグレードできるので、2.99であなたのテストコード一式を実行してください。
- RSpec 2.99は、3.0で取り除かれたり壊れてしまう変更点の警告を出しますから、この警告に対応してください。2.99で警告が出なくなったら、そこからは何も変更を加えることなく3.0にアップグレードできます。
2.99は重要なステップなので、3.0へのアップグレード手順としては省略しないでください。 それはあなたのテストがどのようにRSpecを使っているかに応じて、あなたに向けにあつらえられたアップグレードのチェックリストです、changelogをあさってRSpec 3で何が変わったか探そうとするよりも、あなたによりシンプルで効率的なアップグレード方法を提供してくれるでしょう。
開発とリリースの計画について
我々は既にそれぞれのmasterブランチでRSpec 3に向けての取り組みを開始しています。 我々はまた、2.14での変更用に2-14-maintenanceブランチ(パッチリリースを可能にするため)、2.99での変更に向けて2-99-maintenanceブランチをもっています。 我々は最終的な3.0のリリースに向けて複数のRelease Candidates版(といくつかのβ版)のリリースを計画しています。
RSpec3のリリースがいつ頃になるかを予想するような危険を冒すのはやめておきます。 経験からいうとリリース日のみつもりはたいてい間違っています :(
How can i help?
(訳者より: 手伝いたい方はきっと英語も読めるだろうと思うのでここは訳しませんლ(╹◡╹ლ)