私がMinitestとRailsのFixturesにハマった7つの理由
Brandon Hilkertの7 reasons why I'm sticking with Minitest and Fixtures in Railsの雑な日本語訳です。
誤訳が雑すぎる訳やあれば、Twitterで@nilp_までお声かけいただくか、コメントやブコメで指摘してもらえると幸いです。
私がMinitestとRailsのFixturesにハマった7つの理由
私は先月グリーンフィールドのRails 4.1アプリの唯一の開発者として過ごす機会に恵まれました(訳注: なんの制約もないことをGreenfieldと言うそう)。 既存のコードをメンテするのにかなりの時間を費やしたことのある人にとって、 パターンを設け、ツールを選択する自由があることは非常に喜ばしい変化です。 私が行った選択の1つがMinitestとRailsのfixturesを使うことです。
端的にいうと、それは最高でした! どんだけヤバかったかというと私が将来それ以外のものを使うことが想像出来ないぐらいです。
背景
私は2009年にRailsをはじめました。
当時、他に誰も使っていないように感じましたが、Railsのコアチームはtest_unit
を使っていました。
私はそれが真実ではないと知るまで、Rspecの方がテスティングフレームワークとして圧倒的な人気を誇ってるようにみえていました。
Ruby/Railsを覚えるまでテストの経験を持たなかった身として、 私はどうやってテストするかそしてなにをテストするかを学ぶための情報を探していました。
当時(...そして今もなお)、Rubyのテスティングテクニック、 とりわけRailsアプリケーションの何をどうやってテストするかにフォーカスを当てた本はあまり多くありません。
なので、私がThe Rspec Bookに出会ったとき、 私はRubyでテストを書くための概念とベストプラクティスを理解するのに役立ちそうな格式ばった本にようやく出会えたことに興奮しました。 それにくわえて、ほとんどのRailsのチュートリアルはRSpecをインストールするところからスタートするようにみえました。 私は何をどうすればいいか全くわからなかったので、流れに従いRSpecを私のテスティングツールボックスの中心に据えました。
私は、考えてみれば、Test::UnitやMinitestに公平な機会を与えなかったことを認めます。 わたしはすぐにRSpecの深みにはまりました、理由があるわけでもなく、最近まで他の選択肢について考えることもありませんでした。 幸いなことに、最近ではより多くの人がMinitestを知っており、取っ掛かりにふさわしい方法だと考えているように見えます。
私の以前の設定
私の前のプロジェクトの典型的なGemfile
はこんな感じです:
group :test do gem "rspec-rails" gem "factory_girl_rails" gem "capybara" gem "selenium-webdriver" gem "database_cleaner" gem "shoulda-matchers" end
ほかのgemはさておき、私のspec_helper.rb
はトランザクションをオフにするなど(database_cleaner
のため)Railsの標準的なテスト規約に沿わないいくつもの設定がありました。
この設定はメチャ複雑で、複雑さを管理するためのgemがあるくらいです。 フレームワークのために数えきれないほど多くの規約があって、私にとってそれはまともには思えませんでした。
以下は私が1ヶ月をMinitestとfixturesをRails 4.1のアプリケーションに費やした結果得られた知見です(順番に特に意味は無いです):
1. Fixturesは "本物" のデータに対してテストするよう強制します
フィクスチャデータは現実のものではないです。 それは準備されたものであなたは何をどれぐらい加えるか管理することができます。
私は家族向けのテレビ番組や映画のように共通の対象を中心に据えることで、 あなたが既に関係するキャラクターのヒエラルキーを思い描いているように、 アプリケーションの中身へと素早く入り込むことが出来ることに気づきました。
以下は私のusers.yml
フィクスチャから抜き出したサンプルです:
fred: first_name: Fred last_name: Flintstone email: fred@flintstone.com title: CEO password_digest: <%= ActiveRecord::FixtureSet.default_password_digest %> company: flintstone confirmed_at: <%= Chronic.parse("1/1/2014") %> invitation_token: <%= SecureRandom.urlsafe_base64 %> wilma: first_name: Wilma last_name: Flintstone email: wilma@flintstone.com title: COO password_digest: <%= ActiveRecord::FixtureSet.default_password_digest %> company: flintstone supervisor: fred invitation_token: <%= SecureRandom.urlsafe_base64 %>
興味深いことに、昔はアソシエーションにおいてつらみがあったかもしれませんが、
flintstone
をcompanyの名前として使えて、そしてそれが同じ名前のcompany
フィクスチャを参照するとしたらどうでしょうか:
flintstone: name: Flintstone Inc. phone: 888-555-1212 updated_at: <%= 6.months.ago %> created_at: <%= 6.months.ago %>
アプリケーションの全て時点において、私にはテストのためによく構造化されたデータがあります。 Factory Girlを使うと、もしあなたがアソシエーションとシードデータを持つような複雑なデータ構造をつくろうとした場合、イライラさせられます。
ActiveRecordのアソシエーションが複雑になったとき、 私はそれぞれのテストでセットアップを行うせいでイライラさせられること、 そして初期データを作るためにかかる時間が増えていくことに気づきました。
これを回避するため、既に設定されたアソシエーションと共にfactoryを使う方法がありますが、私はそれがすぐにあなたをstubに推し進めてしまうと思っています。 私はstubしてる場所が実装ベッタリなとき、たとえqueryが同じデータを返すとしても、検索条件を1つ変えるだけでテストが落ちてしまうのを直に見てきました。
最近のTDDについてのディスカッション では似たような話題についてたくさんの議論を呼びました。
総じていうと、私はmockとstubが少ないほど、私の書いたテストに自信を持つことができます、 そしてfixturesを使ったとき私はしばしばmockとstubは必要ないと感じました。 私のデータは想定の範囲内です、だから私は私のテストに自信を持てます。
最後になりますが、それぞれのテストの前にFactory Girlがデータを挿入するとき、 そこにはデータベースとやりとりするためのコストがかかります。 そのコストはそれを何度も行うことで増えていき、あなたのテストスイートは遅くなります。 Fixturesはテストスイートが実行される前に挿入されます、なのでテストに固有の変更がないかぎり、 概ね追加の挿入が必要になることはありません。 これによりもたらされる利益は最初は微々たるものですが、その利益は時間が経つにふえ、あなたの手元には よりパフォーマンスがよく、より頻繁に実行されやすいテストスイートが残るでしょう。
注: 私はbuild_model
のような、データベースに触れずARオブジェクトを作るメソッドがあることについては知っていますが、テストではデータベースのデータを使う必要がある場合(例えば、スコープや、状態が変化するメソッドなどなど)があります。
言うまでもないと思いますが、あなたは簡単にfixturesのデータをdevelopment環境にロードして使えます。
2. RSpecは何かをするために複数の方法を提供します
これは例えば2つのものが同じかどうかをどうassertするかといった単純なトピックでさえ、混乱を引き起します。
あなたは、そして私はeq()
か==
かあるいはeql()
のどれを使えばいいのでしょうか? 誰も分かんなくない???
"?"で終わるメソッド周りのファンシーな記法についてはどうでしょう?
post.should be_active
ちょっとまった、そう、どこにbe_active
メソッドがあるのでしょう?
いいやっ! テストにおいてアプリケーションに本当にちゃんとactive?
があるか確かめるために、RSpecはメソッドをパースしています。
最初は、その魔法に魅了されるでしょう。
しかし、そのあとで、単にactive?
とタイピングしてそれをtrue
かfalse
とアサートするべきだときづいたとき、
私は何をどうやって書くかに悩まされすぎていることに気づきました(もちろんまったく違う方法でこれを書くことも出来るでしょう)。
post.active?.should be_true
...あるいはこう:
post.active?.should == true
これで十分かって?
私はシンプルなことの良さと、一般的にassertionを書く方法はただ1つしか無いことを学びました:
assert post.active?
正しいassertionの書き方についてこれ以上ぐだぐだするのはさておいて、私はこの方法がテスト中のシンタックスエラーを少なくすることに気づきました。 アプリケーションコードをテスト出来るまでに、2回も3回もテストのエラーが出るのはイライラするし時間の無駄です。 同じことをやるときに限られた方法を使うことは、私がテストを書いているときにシンタックスエラーを起こすことを少なくしてくれました。
3. Capybaraのセットアップは簡単です
あなたが過去にカピバラを使用したことがあるなら、 あなたはそれをFactoryGirlと合わせて使うのは...奥が深いことを知ってるでしょう。
database_cleanerの設定に関するAvdiの記事 を私も設定しました:
RSpec.configure do |config| config.before(:suite) do DatabaseCleaner.clean_with(:truncation) end config.before(:each) do DatabaseCleaner.strategy = :transaction end config.before(:each, :js => true) do DatabaseCleaner.strategy = :truncation end config.before(:each) do DatabaseCleaner.start end config.after(:each) do DatabaseCleaner.clean end end
もちろん、この設定は、まずspec_helper.rb
でtransactionを無効にしたあとやります:
config.use_transactional_fixtures = false
なぜ、このすべての設定が必要なのか?
あなたがFactory Girlを使用し、テスト中にデータベースにデータを挿入する場合、それはトランザクション内で行われます。 テストが終わったとき、トランザクションはロールバックされデータは永続化されずに白紙の状態で次のテストがはじまります。 これはうまく正しく動くように見えるでしょう? でも全てがうまくいくわけではないのです...
もしあなたがseleniumのようなJavaScriptが有効なドライバーをインテグレーションテストを実行するために使う場合、 ブラウザの操作は違うスレッドで実行されます。
これでは他のスレッド/トランザクション中で設定されたデータを見ることはできません。
以上の理由により、あなたはtruncation strategyを頼らざるをえないのです。 それゆえ、以下のような設定になります:
config.before(:each, js: true) do DatabaseCleaner.strategy = :truncation end
これは複雑で初心者にはわけがわかりません。 更に、あなたがマルチスレッドのコードあるいはgemを使っている場合、話は余計にややこしくなります。
あなたがかわりにfixtureを使うことにした場合、データはそれぞれのテストの開始時に挿入され(独立したトランザクション中ではない場合)、 そのデータは引き続き実行されるどのスレッドでも、ブラウザの操作でも、どこでも利用可能です。
factoryの代わりにfixturesを使う場合、通常の場合はdatabase_cleaner
を使う必要がなくなります。
それに加え、Capybara
を使うために必要な設定はtest_helper.rb
に以下の内容を追加するだけです:
require "capybara/rails" class ActionDispatch::IntegrationTest include Capybara::DSL end
これだけなのです...マジで...私がヤバイと思うのは、この設定はRailsのテストスイートでCapybaraとRSpecを使う場合、非常にありふれた設定だということです。
私達はRailsが、我々がぶち当たるような一般的な問題のためのソリューションを提供してくれていることに甘んじていました。
truncating vs transactionsを決めることや、database cleanerの複雑な設定を行っていることは、理にかなっておらずRails体験に反しているようにみえます。
私は率直にいって、長い間これが理にかなっているように見えていたことに驚いています。
FactoryGirlのような慣習/ツールはコミュニティーを助けるというより逆に傷つけてしまっている可能性はないでしょうか?
4. 複雑なスタブ/モックをしないことがコードをシンプルにする
RSpecはいつでも/どこでも簡単にstubをすることができます(上の方の#1みたいに)。 これにはいくつかの利点がありますが、簡単に腐った使い方が出来てしまいます。 実際、私はfixturesを使うことで、それほど頻繁にstubを使わなくなりました。 顧客がアプリケーションを訪れていい体験をしたとき、顧客はそのデータがスタブされているかどうかなんて全く気にしてません。 私のテストがデータとアプリケーションに対して動いていているという事実を毎日見ることは、 新しいコードがプロダクションに行ってもうまく動くという自信を私に与えてくれました。
Minitestは読みやすく使いやすいモッキングライブラリをもっています。
それはRSpecが最初からもってるほど多くの機能があるわけではないですが、
しかし、あなたがより多くの機能をもとめるなら、
mocha
gemやそれと同じようなものを使えます(私は必要なかったです)。
5. Minitestのassertionの順番がわからないときスニペットが助けてくれる
私が最初にMinitestにとりかかったとき、私の頭のなかには長い間「どちらが期待する値でどちらが実際に得られた値なのか」という疑問が浮かんだままでした。 今は、私は補助がなくてもそれについて熟知しています、しかし、必要もないのにめっちゃタイプすることもないでしょう。
私は引数の順番を覚えておく苦痛を取り除いてくれるVimのためのRubyスニペットをすごい頼ってます。
6. MinitestはただのRuby
公平を期すためにいうと、RSpecも同じくただのRubyです。 しかし一般的にいえばRSpecは全てにおいて―shared examplesや、テストのセットアップや、設定など―魔法のような方法に見えます。
これらは全て "RSpec way" を持っているようにみえます。
Minitestでは単にRubyを使ってこれを扱います。もしあなたがshared examplesが必要なら、 共有したいテストを含んだモジュールをインクルードすればいいですよね?
私は最初の数日間のMinitestの経験で、何かをするための "正しい" 方法を探しているのに気づきました。 私は、尊敬しているコミュニティの人たちに諭され、それがただのRubyなんだと実感しました。 その考え方は私に、RSpecのDSLによる魔法を使うというより、その言語を使ってやりたいこと全てをやっていいんだと気づかせてくれました。
ある意味では、たくさんの魔法は私達の視野を狭めてしまうと思います。 いくつかの素敵なものを使って(悪い使い方もして)、我々は、自分たちが使っているツールが、我々の全ての問題を解決してくれるんだと信じはじめていました。 Minitestを使うことがこの考え方を打ち砕き、私が培ったRubyのスキルに依って私がテストする上での課題を解決出来るということを認識させてくれました。
7. Railsのデフォルトから脱線することが、いつも価値を提供するわけではない
もちろん、Railsの全てが理想通りとではないことはわかります。 ですが、強い信念によって作られたもののによってどれだけ多くの人が救われているかを考えてみると、それは賞賛に値します。 先月、Railsのデフォルトスタックに頼るようになってから、私は私がツールを選ぶときにどれだけシンプルかを考慮していなかったことを実感しました。
私はコミュニティがRSpecとFactory Girlを使っていることが多いこと(私にはそう見えていた)をツールを選ぶ基準にしていましたが、それはいい考えでした。
一方で、シンプルさとコミュニティの大きさ両方の面で考えることができたなら、私はRSpecを使うことはなかったでしょう、 私はそれがいいものだと思っていましたが、それは単に他のものを知らなかっただけです。
RailsのレールにのってデフォルトスタックのMinitestとFixturesを使うことの手軽さは私の考えを変えました。 そこには最小限の設定があって、ほとんどの必要なことは非常に小さな追加の設定をするだけですみました。
私はまだ他の人が話してるようにfixtureを使うつらみを感じたことはありません。 しかし私は前もって考えもせずにデータに大きな変更をもたらさないよう気をつけてきました。
たぶん、このアプリがまだ十分に大きくなっていないから? 私のデータが十分複雑になっていないから? あるいは私がデータの変更がもたらす影響に十分注意しているからなのか?
それがなんであれ、今のところ私にとってそれは機能しています。 「 うおおおお! これがみんながRSpecとFactory Girlを使う理由か。 」というときが来るかもしれないと感じることもありますが。 もっとも、今の時点では想像もつきません。 デフォルトスタックは私にとって機能してるし私が好きなんだから、そんなことはどうでもいいんだよ。
まとめ
私はあるフレームワークと他のフレームワークのパフォーマンス合戦について言及するのを意図的に避けました。 しかし、このトピックについてこのspeakerdeckは素晴らしいベンチマークを提供しています。
私は私のツールキットにMinitestとfixtureを加えることが出来てとても興奮しています。 私がこれまでのところ見てきた利益の前では、私が将来これ以外の別のものを選択し使うことは想像できません。
もしあなたがデフォルトのMinitestのアサーション記法に不満なら、 あなたはMinitestがspecスタイルの書き方をオプションで使えることを知って嬉しくなるでしょう。 もちろんそれはRSpecのシンタックスと全く同じものではありませんが、それはより自然言語っぽい書き方に近づけてくれるでしょう、もしもあなたにとって自然言語っぽさが大事ならね。
RSpec 3の重要な変更
Myron Marston » Notable Changes in RSpec 3の雑な訳です。
誤訳・雑すぎる訳がありましたら、Twitterで@nilp_までご連絡頂けると助かります。
RSpec 3.0.0 RC1が2日前にリリースされました、そして最終的な3.0.0のリリースが目前に迫っています。 我々はβ版をここ6ヶ月にわたり使ってきました、我々はそれらを皆さんと共有できることにわくわくしています。
これが新しいとこだよ:
すべてのgemたちにわたって
Ruby 1.8.6と1.9.1のサポートがなくなりました
これらのバージョンのRubyはかなり前に寿命を迎えました、RSpecはこれらをサポートしません。
Ruby 2.xのサポート向上
最近のRSpec 2.xのリリース(すなわち2.0がリリースされたあと出たやつ)はRuby 2を公式にサポートしています、しかしRSpec 3でのサポートはより向上しました。 現在我々はRuby 2の新機能のためのサポートを提供しています、たとえばキーワード引数やprependされたモジュールなどです。
新gem rspec-support
rspec-supportは、我々が1つ以上のrspec-(core|expectations|mocks|rails)で必要としている共通したコードのために使用している新しいgemです。 それは今のところエンドユーザーあるいは拡張ライブラリの作者が使えるようなpublicなAPIは含まれていません、しかし我々は将来いくつかのAPIをpublicにするかもしれません。 もしあなたがGemfileでGitHubをソースに指定して、最新版RSpecを実行しているなら、あなたはrspec-supportのために同じ指定をする必要があります。
堅牢で、よくテストされたアップグレードプロセス
RSpec 3における全ての破壊的な変更は2.99でのdeprecation warningに相当します。 βの間、我々はアップグレードが出来る限りスムーズに進むよう多くのアップグレードを行いました。 我々は段階的にアップグレードするための説明を1つにまとめてあります。
アップグレードのプロセスはRSpecのすごく設定の柔軟性が高い、新しいdeprecationシステムにも目を向かせます(deprecationをファイルに出力したり、全てのdeprecationをエラーにできる)、 そして、それは重複したdeprecationの出力を少なくするように設計されています。
改善されたドキュメント
我々は全てのgemのAPIドキュメントをアップデートするためクソ頑張りました。 それらは現在rubydoc.infoでホストされています:
...しかし、現在われわれはこれらをセルフホストするために、rspec.infoの更新を行っています。
ドキュメントは今も作業中です(実のとこいつも作業中)、
我々はSemVerの一環として全てのパブリックAPIを明確に公表できるようにしました。 我々は全ての3.xのリリースの間、全てのパブリックAPIをメンテナンスするために全身全霊を尽くしています。 一方で、プライベートAPIについては、3.xのリリース中のどこかでそれらを変更できるような柔軟性を確保したかったのでプライベートとなっています。
どうか、我々がプライベートと宣言したAPIを使わないでください。 もしあなたが既存のpublic APIによって取り上げられていないニーズを見つけたら、どうぞ尋ねてください。 我々はなたのニーズのため喜んでプライベートAPIをpublicにするか、新しくあなたのユースケースに合ったAPIをつくるでしょう。
Gemに署名がついたなう
我々は私達のgemに署名をつけはじめました。 しばらくはgemの署名システムは理想とは程遠いですが、 よりよいソリューションが開発されはじめています、 しかしないよりはマシです。 我々は私達の公開鍵をGitHubに置いています。
現在のgem署名システムについてより詳しいことが知りたければ、A Practical Guide to Using Signed Ruby Gemsを見てください。
ゼロモンキーパッチモード
RSpecは今いかなるモンキーパッチもなしで使うことができます。
このための大部分の土台が新しくexpect
ベースの記法をrspec-expectationsとrspec-mocksに追加した最近の2.xのリリースで作られました。
我々はRSpec 3への道のりで、残りの部分をとりのぞき、残っているモンキーパッチについても代替を提供しました。
便利なことにあなたは全てのモンキーパッチを1つのオプションで無効にできます:
spec/spec_helper.rb
RSpec.configure do |c| c.disable_monkey_patching! end
この設定オプションを実装してくれてありがとうAlexey Fedorov。
より詳しい情報:
rspec-core
フックスコープのための新しい名前: :example
と :context
RSpec 2.x は3つの異なるフックスコープをもっていました:
my_class_spec.rb
describe MyClass do before(:each) { } # このグループのそれぞれのexampleの前に実行される before(:all) { } # このグループの最初のexampleの前に1度だけ実行される end
spec/spec_helper.rb
RSpec.configure do |c| c.before(:each) { } # 全てのテストスイート中のそれぞれのexampleの前に実行される c.before(:all) { } # それぞれのトップレベルのグループの最初のexampleの前に実行される c.before(:suite) { } # 全てのspecファイルがロードされたあと、最初のspecが実行される前に一度だけ実行される end
ときどき、ユーザーは:each
vs :all
が何を意味するか混乱することと、特に:all
をconfigブロック中で使ったときに混乱することを述べていました:
spec/spec_helper.rb
RSpec.configure do |c| c.before(:all) { } end
このcontextでは:all
という言葉はテストスイート中の全てのexampleの前に一度のみ実行されることを推測してしまいます、しかしそは:suite
のことです。
RSpec 3では、:each
と:all
はスコープをより明確にするエイリアスを持っています:
:example
は:each
のエイリアスで:context
は:all
のエイリアスです。
:each
と:all
はdeperecatedではなく我々がそうする計画もないことに注意してください。
これを実装してくれてありがとうJohn Feminella。
より詳しい情報:
DSLメソッドがexampleを引数として渡す
RSpec::Core::Example
はexampleに関する全ての詳細にアクセスする手段を提供します: その説明、場所、メタデータ、実行結果、などなど。
RSpec 2.xではexampleはexample
メソッドとしてどんなフックや個別のexampleからでもアクセス出来るようにさらされていました:
my_class_spec.rb
describe MyClass do before(:each) { puts example.metadata } end
RSpec 3では、我々はexample
メソッドを取り除きました。
その変わりに、instanceは全てのexampleスコープのDSLメソッドにはっきりと引数として渡されるようになりました。
my_class_spec.rb
describe MyClass do before(:example) { |ex| puts ex.metadata } let(:example_description) { |ex| ex.description } it 'exampleにアクセス' do |ex| # exを使う end end
それをアイデアと共に実装してくれてありがとうDavid Chelimsky!
より詳しい情報:
rspec-coreのモンキーパッチングを無効にするための新設定オプションexpose_dsl_globally
RSpec 2.xはトップレベルメソッドのdescribe
や、shared_examples_for
やshared_context
を提供するためmain
とModule
をモンキーパッチしていました。
my_gem_spec.rb
shared_examples_for "something" do end module MyGem describe SomeClass do it_behaves_like "something" end end
RSpec 3では、これらのメソッドはRSpec
モジュール上で利用出来るようになりました(それに加えてモンキーパッチとしても利用可能です)。:
RSpec.shared_examples_for "something" do end module MyGem RSpec.describe SomeClass do it_behaves_like "something" end end
新しいexpose_dsl_globally
の設定オプションをfalse
に設定することによって、あなたはrspec-coreのモンキーパッチを完全に取り除くことができます(それにより上で出てきた最初のexampleはNoMethodError
の例外を投げるようになります)。
spec/spec_helper.rb
RSpec.configure do |config| config.expose_dsl_globally = false end
より詳しい情報:
alias_example_group_to
でexample groupにエイリアスを定義できます
RSpec 2.xでは、我々はあなたがメタデータ付きのexample
のエイリアスを定義できるAPIを提供しました。
例えば、これは:forcus => true
のメタデータを伴ったit
のエイリアスのfit
を定義するために内部で使われていました:
spec/spec_helper.rb
RSpec.configure do |config| config.alias_example_to :fit, :focus => true end
RSpec 3では、我々はexample groupsでもこの機能を利用出来るよう拡張しました:
spec/spec_helper.rb
RSpec.configure do |config| config.alias_example_group_to :describe_model, :type => :model end
あなたはこの例をrspec-rails
を使っているプロジェクトで利用し、describe User, :type => :model
のかわりにdescribe_model User
を使えます。
これを実装してくれてありがとうMichi Huber
より詳しい情報: - Documentation - rspec-core #493 - original discussion
example groupの新しいエイリアス: xdescribe
, xcontext
, fdescribe
, fcontext
example groupのエイリアスを定義するためのAPIを加えるだけではなく、我々は同様にいくつかの追加のビルトインエイリアスを加えました(describe
とcontext
上に):
xdescribe
/xcontext
、たとえばxit
のようなものです、一時的に1つのexampleグループをスキップするために使えます。fdescribe
/fcontext
、たとえばfit
のようなものです、一時的に1つのexampleグループに:focus => true
のメタデータを加えるために使えます、あなたはconfig.filter_run :focus
によって関心を持ったexamplesとgroupsを簡単にフィルターできます。
より詳しい情報:
pending
の意味の変更(そしてskip
の紹介)
Pending exampleは今、ほんとは通っているのかをチェックするために実行されます。 もしpendingブロックが失敗したら、それは前と同じようにpendingとして記録されます、 一方、もしそれが成功した場合、それは失敗したことになります。 これはexampleらを保留にしたことと、それらが説明していることが実装されたとき保留を迅速に解決することを確実にする手助けをします。
"決して実行しない"古い動作をサポートするために、skip
メソッドとメタデータが加えられました。
次のexampleらはどれも実行されることはありません:
post_spec.rb
describe Post do skip 'not implemented yet' do end it 'does something', :skip => true do end it 'does something', :skip => 'reason explanation' do end it 'does something else' do skip end it 'does something else' do skip 'reason explanation' end end
この変更によって、あるexample中でpendingにブロックをわたすことは納得いかない動作になりました、なのでその動作は取り除かれました。
これを実装してくれてありがとうXavier Shay。
より詳しい情報:
ワンライナーのための新しいAPI: is_expected
RSpecはワンライナーのための記法を長い間持っていました:
post_spec.rb
describe Post do it { should allow_mass_assignment_of(:title) } end
このcontextでは、should
はモンキーパッチされていませんがshould
は:expect
記法のみをサポートするrspec-expectationsの設定によって取り除くことが出来ます。
この場合はshould
を使えるようにObject
をモンキーパッチするのに嫌な気はしません、これはあなたの記法の設定にかかわらず常に利用可能です。
何名かのユーザーはもしこれを使い続けることが出来るなら、このshould
とexpect
記法をどう関連づけるのかについて戸惑いをみせました。
これはRSpec 3でも利用可能なままです(もう一度いいますがあなたの記法の設定に関係なく)、しかし我々は同様にexpect
記法に矛盾しない代替のAPIを追加しました:
post_spec.rb
describe Post do it { is_expected.to allow_mass_assignment_of(:title) } end
is_expected
はexpect(subject)
として非常に簡単に定義されており、should_not
と同様に否定する検証をis_expected.not_to
マッチャーによりサポートします。
より詳しい情報:
Exampleグループの実行順を個別に設定できる
RSpec 2.8からrandomな実行順をRSpecに取り入れました、これはあなたのspecスイート中にある故意ではない実行順依存を明らかにするのに非常に役に立つでしょう。 RSpec 3では、単にオールオアナッシングの機能ではなくなりました。
あなたはexampleグループごとに合ったメタデータでタグ付けし、個別に実行順序をコントロールすることができます。
my_class_spec.rb
describe MyClass, :order => :defined do # 実行順の設定にかかわらず、 # このグループ中のexampleらは常に定義された順序で実行されます。 end describe MyClass, :order => :random do # 実行順の設定にかかわらず、 # このグループ中のexampleらは常にランダムな順序で実行されます。 end
これは特に実行順を定義順からランダムな順序に切り替えるのにやくだちます、 あなたは問題を一度にすべて解決しようとせず、特定のグループに対してこの機能を用いていくことで、実行順序の依存を1つずつ解決していくことが可能です。
この一環として、我々は同様に--order default
の名前を--order defined
に変更しました、なぜなら我々は "default" が非常に多岐に渡る意味を持つと実感したからです。
この機能を実装するのを助けてくれてありがとうAndy LindemanとSam Phippen。
より詳しい情報: - Documentation
新しいオーダーリングストラテジーAPI
RSpec 3では、我々はオーダーリングストラテジーAPIをオーバーホールしました。
依然は
3つの、
異なる、
メソッド
だったのが1つのメソッドになりました: register_ordering
です。
オーダーリングストラテジーに名前をつけるためにこれを使ってください。
spec/spec_helper.rb
RSpec.configure do |config| config.register_ordering(:description_length) do |list| list.sort_by { |item| item.description.length } end end
my_class_spec.rb
describe MyClass, :order => :description_length do # ... end
あるいはあなたはこれをグローバルなオーダーリングとして使うことができます:
spec/spec_helper.rb
RSpec.configure do |config| config.register_ordering(:global) do |list| # アルファベット順にソートする list.sort_by { |item| item.description } end end
:global
のオーダーリングはトップレベルのexample groupsと、:order
のメタデータをもたない全てのexample groupsを順番付けるために使われます。
より詳しい情報:
rspec --init
のカイゼン
rspec
コマンドは長い間、プロジェクトスケルトンをセットアップするために--init
オプションを提供してきました。
RSpec 3では、そのコマンドが作成するファイルが、箱から出してそのまま使えるよりよいものを提供するため、かなりカイゼンされました、そして推奨する設定と一緒にspec/spec_helper.rb
ファイルを提供します。
あ、推奨する設定の中でデフォルトになる予定がないものは生成されたファイル中でコメントアウトされています、 なので、ファイルを開いて、あなたがいいな〜と思うオススメ設定を受け入れるといいですよ。
より詳しい情報:
新しいコマンドラインオプション --dry-run
このオプションはいかなるexampleもhookも実行せず、あなたのspecスイートをformatterを使って出力した結果を画面に表示します。 これは、テストが通る/落ちるを気にしたり、テストが実行されるのをまったりせずに、あなたのテストスイートのドキュメント化されたアウトプットをレビューするのに特に役にたちます。
これに貢献してくれてありがとうThomas Stratmann!
より詳しい情報:
フォーマッターのAPIが変わりました
より柔軟な完全に新しいフォーマッターAPIが追加されました。
- あなたが欲しいイベントのみを受け取れます
- メソッドは、具体的なパラメーターではなく通知オブジェクトを受け取ります、なので後方互換性のマナーにのっとり新しい通知データを追加することが出来るようになりました
- ヘルパーメソッドは通知オブジェクト上で使えるようになったので
BaseTextFormatter
を継承する必要がなくなりました
新しいフォーマッターはこんな感じになるでしょう:
custom_formatter.rb
class CustomFormatter RSpec::Core::Formatters.register self, :example_started def initialize(output) @output = output end def example_started(notification) @output << "example: " << notification.example.description end end
古い2.xのフォーマッターAPIを引き続きサポートするためにrspec-legacy_formatters gemが提供されます。
この役を引き受けてありがとうJon Rowe。
より詳しい情報:
アサーションの設定が変わりました
ほとんどのユーザーはrspec-expectations
を使っていますが、実は他のものを使うこともできます、RSpec 2.xは一番一般的な代替を設定オプションで簡単に利用することができました:
spec/spec_helper.rb
RSpec.configure do |config| config.expect_with :stdlib # あるいは、両方を使う config.expect_with :stdlib, :rspec end
さて、この:stdlib
周りで混乱がありました。
Ruby 1.8では、標準のアサーションモジュールはTest::Unit::Assersions
です。
1.9+では、それはMinitesst::Assertions
上の薄いラッパーです(あなたが単にそれだけを使うならうまくいきます)。
そうしているうちに、minitest gemとtest-unit gemも同様にTest::Unit::Assertions
を定義しました。
RSpec 3では、我々はexpect_with :stdlib
を取り除きました、その代わりに明確に:test_unit
か:minitest
オプションを選択するようにしました:
spec/spec_helper.rb
RSpec.configure do |config| # for test-unit: config.expect_with :test_unit # for minitest: config.expect_with :minitest end
これを実装してくれてありがとうAaron Kromer。
より詳しい情報:
〜由来のメタデータ定義
RSpecのメタデータシステムは超絶に柔軟で、あなたはテストスイートをいろんな切り口で扱うことができます。
この新しい設定APIは、あなたが〜由来のメタデータを定義することを可能にします。
例えば、spec/acceptance/js
以下の全てのexample groupを:js => true
でタグづけしたりできます:
spec/spec_helper.rb_
RSpec.configure do |config| config.define_derived_metadata(:file_path => %r{/spec/acceptance/js/}) do |metadata| metadata[:js] = true end end
より詳しい情報:
なくなったもの
いくつかのものについては、RSpecのコアに含まれなくなり、全て取り除かれるか外部gemとして切りだされました:
- TextmateフォーマッターはTextmate bundleに移動しました。たった1つのテキストエディタのためのフォーマッターをrspec-coreの中に持つのは筋が通りません。
- RCovとの統合はなくなりました。これが1.9+で動くようにアップデートされることはありませんでした、最近では我々は代わりにsimplecovを使うことをおすすめしてます。
- コマンドラインオプションの
--debug
をとりのぞきました。最近では非常におおくのデバッガーの選択肢があります、そしてあなたはそれらをコマンドラインから--require
(あるいは-r
)オプションを使って有効にできます。例えば、byebugを使うには、-rbyebug
をコマンドラインで渡します。 - 我々はコマンドラインオプションの
--line-number
をとりのぞきました。これは先頭にある場合意味がはっきりせず(--line-number 43
はロードされた全てのspecファイルの43行目の近くで定義されたexampleを選択するが、それぞれのファイルの43行目が関係ある理由がない)、よりぶっきらぼうな感じのpath/to/spec.rb:43
の形と重複しています。 its
が新しいrspec-its gemに切りだされました、これは親切にもPeter Alfvinがメンテナンスすることを申し出てくれました。- Autotestとの統合が新しいrspec-autotest gemに切りだされました(これにはメンテナーが必要です: だれかボランティアはいませんか?)。
rspec-expectations
should
記法を明示的に有効にせずに使うのはdeprecatedになりました
RSpec 2.11で我々は新しいexpectベースの記法を導入することで、モンキーパッチをRSpecから外す方向に進みはじめました。
RSpec 3では、我々はshould
記法をもち続けます、そしてそれはデフォルトで利用可能です、しかしあなたが明示的にそれを有効にしない場合、あなたはdeprecation warningを受けとるでしょう。
これは、古いチュートリアルで新しくRSpecを学びはじめた人の混乱を避け、RSpec 4でデフォルトでそれを無効にするため(あるいは別のgemとして切り離す可能性もあります)の状況を整えるでしょう、
我々はexpect
記法をRSpecの "main" の記法にすることをきめました、しかしあなたが古いshould
ベースの記法を好むなら、それを使い続けることもできます: 我々にはそれを無くしてしまう計画はありません。
これを実装してくれてありがとうSam Phippen。
より詳しい情報:
マッチャー合成式
RSpec 3では、あなたは複数のマッチャをand
とor
を使ってつなげて一緒にすることができます:
compound_examples.rb
# この2つのexpectationsが... expect(alphabet).to start_with("a") expect(alphabet).to end_with("z") # ...ガッチャンコして1つの式になれる expect(alphabet).to start_with("a").and end_with("z") # もちろん`or`も同じように使えます: expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
compound_operator_examples.rb
expect(alphabet).to start_with("a") & end_with("z") expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")
この機能を提案し実装してくれてありがとうEloy Espinaco、そして&
と|
演算子でそれを拡張してくれてありがとうAdam Farhi。
より詳しい情報:
コンポーザブルマッチャー
RSpec 3は、マッチャーを引数として他のマッチャーに渡すことで、あなたの意思を詳細に表現することを可能にします。
composed_matcher_examples.rb
s = "food" expect { s = "barn" }.to change { s }. from( a_string_matching(/foo/) ). to( a_string_matching(/bar/) ) expect { |probe| "food".tap(&probe) }.to yield_with_args( a_string_starting_with("f") )
コードの表現と失敗時のメッセージの可読性を向上させるため、ほとんどのマッチャーは、それらの式が引数として渡されたとき、きちんと読めるようなエイリアスをもっています。
より詳しい情報:
- New in RSpec 3: Composable Matchers (訳注: 日本語訳しました→RSpec 3の新機能: コンポーザブルマッチャー)
- rspec-expectations #280 - original discussion
- rspec-expectations #393 - implementation
- API Documentation (including list of matcher aliases)
- Relish Documentation
match
マッチャーをデータ構造に対して使うことができます
RSpec 3以前では、match
マッチャーは#match
メソッドを使って文字列/正規表現のマッチングを行うために存在していました:
match_examples.rb
expect("food").to match("foo") expect("food").to match(/foo/)
RSpec 3では、それは任意のネストした配列/ハッシュのデータ構造のマッチングを追加でサポートします。 ネストの中のどのレベルでも、期待する値をマッチャーを使って表現することができます:
match_data_structure_example.rb
hash = { :a => { :b => ["foo", 5], :c => { :d => 2.05 } } } expect(hash).to match( :a => { :b => a_collection_containing_exactly( an_instance_of(Fixnum), a_string_starting_with("f") ), :c => { :d => (a_value < 3) } } )
より詳しい情報:
- New in RSpec 3: Composable Matchers (訳注: 日本語訳しました→RSpec 3の新機能: コンポーザブルマッチャー)
新しいall
マッチャー
このマッチャーをつかえば、あなたはコレクションの中の全ての要素の何かがtrueであることを記述出来るようになります。引数としてマッチャーを渡します:
all_example.rb
expect([1, 3, 5]).to all( be_odd )
これに貢献してくれてありがとうAdam Farhi
より詳しい情報:
新しいoutput
マッチャー
このマッチャーをつかうと、そのブロックがstdoutもしくはstderrに書く内容を記述できるようになります。
output_examples.rb
expect { print "foo" }.to output("foo").to_stdout expect { print "foo" }.to output(/fo/).to_stdout expect { warn "bar" }.to output(/bar/).to_stderr
これを提案してくれてありがとうMatthias Günther(それにスタートをキメてくれた)、 そしてこの機能をフィニッシュラインに導いてくれてありがとうLuca Pette。
より詳しい情報:
新しいbe_between
マッチャー
RSpec 2は動的な述語サポートを使い、between?
を実装してるオブジェクトに対してbe_between
マッチャーを提供してきました。
RSpec 3で、我々はbe_between
マッチャーを一級市民に押し上げることにしました、これにはいくつかのよい面があります:
- 失敗時のメッセージが単に「
between?(1, 2)
がfalse
を返した」というより、よくなりました - それは、
between?
を実装していなくても、比較演算子を実装したオブジェクトで動作します(e.g.<
,<=
,>
,>=
) - それは
inclusive
とexclusive
2つのモードを提供します。
be_between_examples.rb
# `Comparable#between?`のように、デフォルトだとinclusiveです expect(10).to be_between(5, 10) # ...しかし、あなたはそれをexclusiveにすることもできます: expect(10).not_to be_between(5, 10).exclusive # ...あるいは明確にinclusiveのラベルを付けることもできます: expect(10).to be_between(5, 10).inclusive
これに貢献してくれてありがとうErik Michaels-Ober、 そしてよくしてくれてありがとうPedro Gimenez。
より詳しい情報:
Booleanマッチャーの名前がかわりました
RSpec 2はペアのマッチャーを持っていました(be_true
とbe_false
)これはRubyの条件の意味を反映しています: be_true
はnil
かfalse
以外の全ての値で通ります、そしてbe_false
はnil
かfalse
の場合通ります。
RSpec 3では、我々はこれらをより明確な意味になるようbe_truthy
とbe_falsey
(もしくはbe_falsy
、もしあなたがこのスペリングのほうが好みなら)に変更しました、
そしてbe true
/be false
との混乱をなくしました(be_true
/be_false
と同じように読めるが、ほんとにtrue
/false
のときしか通らない)。
これを実装してくれてありがとうSam Phippen
より詳しい情報:
match_array
マッチャーがcontain_exactly
として利用できるようになった
RSpecは長い間、このマッチャーをあなたが2つの配列の内容を順番を気にせずに比較することに使えるようにしてきました。
もともとは、これは古いshould
記法で=~
を使うことで利用することができました:
match_array_operator_example.rb
[2, 1, 3].should =~ [1, 2, 3]
その後、我々がexpect記法を追加したとき、演算子のマッチャーを新しい記法に持ち込まないことを決めました、そしてこのマッチャーのことをmatch_array
と呼ぶことにしました:
match_array_example.rb
expect([2, 1, 3]).to match_array([1, 2, 3])
match_array
はそのとき私達が一番いいと考えた名前でした、
しかし我々はそれを手放で喜ぶわけにはいきませんでした: "match"は不明確な言葉遣いです、このマッチャーは配列以外の種類のコレクションでもうごくことを意図しています。
RSpec 3で我々はこのマッチャーによりよい名前をつけました:
contain_exactly_example.rb
expect([2, 1, 3]).to contain_exactly(1, 2, 3)
気をつけて欲しいのはmatch_array
はdeperecatedではないことです。
match_array
は1つの配列を引数で受け取る一方、contain_exactly
は展開された要素を個別に受け取る(訳注: *splatどう訳すか)こと以外は、この2つのメソッドはまったく同じです。
より詳しい情報:
コレクション数マッチャーがrspec-collection_matchers
gemに切りだされました
コレクション数マッチャー―have(x).items
、have_at_least(y).items
それにhave_at_most(z).items
―はRSpecの魔法じみて混乱するパーツの1つでした。
それらは切りだされて、rspec-collection_matchers
gemに移されました、
親切にもHugo Baraúnaがメンテナンスするのに名乗りをあげてくれました。
より一般的な代替はコレクションのsizeに検証を置くことです:
collection_matcher_examples.rb
expect(list).to have(3).items # ...これはこのように書けます: expect(list.size).to eq(3) expect(list).to have_at_least(3).items # ...これはこのように書けます: expect(list.size).to be >= 3 expect(list).to have_at_most(3).items # ...これはこのように書けます: expect(list.size).to be <= 3
Minitestとの統合のカイゼン
RSpec 2.xでは、rspec-expectationsは自動的に自身をMiniTest::Unit::TestCase
かTest::Unit::TestCase
にincludeしました、なのであなたは単にMinitestやTest::Unitをロードするだけでrspec-expectationsを使うことができました。
RSpec 3では、我々はこの統合を2つの方法でアップデートしました:
- Minitest 4(あるいはそれ以下)とTest::Unitとの統合が自動的に行われなくなりました。もしあなたがrspec-expectationsをそのような環境で使うなら、あなたは
RSpec::Matchers
をあなた自身でインクルードする必要があります。 - Minitest 5とのよりよく統合が提供されました、しかしあなたは
require 'rspec/expectations/minitest_integration'
でそれを明示的にロードする必要があります
より詳しい情報:
マッチャープロトコルの変更
上で述べたように、RSpec 3では、我々はshould
をrspec-expectationsのメインの記法にすることをやめました。
我々はこれを反映してマッチャープロトコルをアップデートしました:
failure_message_for_should
はいまfailure_message
failure_message_for_should_not
はいまfailure_message_when_negated
match_for_should
(カスタムマッチャDSLのmatch
エイリアス)には変わりのものは用意されず、とりのぞかれました(単にmatch
を使ってください)- カスタムマッチャDSLの
match_for_should_not
はいまmatch_when_negated
それに加え、我々はsupports_block_expectations?
を新しく追加しました、マッチャープロトコルのオプション部分です。
これはもしユーザーがvalueマッチャをブロックexpectation式中で間違ってつかったときに分かりやすいエラーを与えます。
例えば、この変更の前では、be_nil
のようなマッチャを使うときに、blockをexpect
に渡すことで、誤った結果を導いてしまいました:
block_expectation_gotcha.rb
expect { foo.bar }.not_to be_nil # ...これは次と同様の意味です: block = lambda { foo.bar } expect(block).not_to be_nil # ...しかし、ブロックはnilではありません(たとえ`foo.bar`がnilを返すとしても)、 # なのでユーザーの意図が次のようなものだったとしても、expectationが通ってしまいます: expect(foo.bar).not_to be_nil
気をつけてほしいのは、supports_block_expectations?
はマッチャープロトコルのオプションの部分だということです。
ブロックexpectation式で使ってほしくないマッチャのために、あえてこれを定義する必要はありません。
より詳しい情報:
- rspec-expectations #270 - original discussion
- rspec-expectations #373 - implementation
- rspec-expectations #530 - original discussion of supports_block_expectations?
- rspec-expectations #530 - implementation of supports_block_expectations?
rspec-mocks
モンキーパッチしたシンタックスを明示的に有効にせずに使うのはdeprecated
rspec-expectationsと共に、我々は rspec-mocks をゼロモンキーパッチのシンタックスへ向けて進みはじめました。
これらは2.14から導入されました、
RSpec 3では、あなたがオリジナルの記法(e.g. obj.stub
、obj.should_receive
、etc)を明示的に有効にせずに使うとdeprecation warningを出すようになりました(rspec-expectationの新しい記法と同様です)。
これを実装してくれてありがとうSam Phippen
receive_messages
とreceive_message_chain
のための新しい記法
もともとのモンキーパッチの記法には2.14でリリースされた新しい記法にはかけていた機能がいくつかありました。
我々はRSpec 3でこれに対して2つの新しいAPIを提示します: receive_messages
とreceive_message_chain
です。
examples.rb
# 古い記法: object.stub(:foo => 1, :bar => 2) # 新しい記法: allow(object).to receive_messages(:foo => 1, :bar => 2) # 古い記法: object.stub_chain(:foo, :bar, :bazz).and_return(3) # 新しい記法: allow(object).to receive_message_chain(:foo, :bar, :bazz).and_return(3)
これらの新しいAPIの利点の1つがこれらはexpect
と組み合わせても、同じように動くことです、一方で古い記法のstub(hash)
やstub_chain
ではこれと同じことはできません。
これを実装してくれてありがとうJon RoweとSam Phippen。
- Documentation for
receive_messages
- Documentation for
receive_message_chain
- rspec-mocks #368 - discussion of
receive_messages
- rspec-mocks #399 - implementation of
receive_messages
- rspec-mocks #464 - discussion of
receive_message_chain
- rspec-mocks #467 - implementation of
receive_message_chain
double
のエイリアスのmock
とstub
を取り除きました
歴史的経緯により、rspec-mocksはテストダブルを作るために3つのメソッドを提供してきました: mock
、stub
とdouble
。
RSpec 3では、我々はstub
とmock
をとりのぞき単なるdouble
を使うことを選択しました、そしてdouble
の上にさらなる機能を体系だてていくことにしました(veryfying doubleみたいな、下の方をみてください)。
もちろん、RSpec 3はdouble
にたいしてmock
とstub
のエイリアスを提供しませんが、あなたがこれらを使い続けたいならば、これらのエイリアスを定義するのは簡単です:
spec/spec_helper.rb
module DoubleAliases def mock(*args, &block) double(*args, &block) end alias stub mock end RSpec.configure do |config| config.include DoubleAliases end
これを実装してくれてありがとうSam Phippen。
より詳しい情報:
doubleらを検証する
あなたが実際に存在するメソッドのみをstubあるいはモックしていることを保証するため、新しい種類のdoubleが追加されました、それは渡された引数を宣言されたメソッドシグネチャによって確認します。
instance_double
とclass_double
、そしてobject_double
のdoubleらは、これらの条件が満たされない時すべてにおいて例外を投げます。
もしクラスがロードされていないとき(通常はユニットテストを独立して実行している際)、例外が発生することはありません。
これは複雑な動作です、しかし独立したユニットテストの速度を高め、インテグレーションテスト(あるいは型システム)に近づくにつれ信頼でき、非常に強力です。
これは大変奇妙な動作に見えるかもしれませんが、独立したユニットテストに速度と、インテグレーションテスト(あるいは型システム)に近い信頼性を与えます。(訳注:ここらへん訳が不安)
これらの新しくよりパワフルなdouble型を使うべきではない理由はあまりないでしょう。
この機能のアイデアと実装をありがとうXavier Shay
より詳しい情報:
部分的なdoubleの検証の設定オプション
doubleらを検証する動作は部分的なdoubleにおいてはグローバルに有効になっています。
(部分的なdoubleとはあなたが既存のオブジェクトをmockあるいはstubしたときです: expect(MyClass).to receive(:some_message)
)
spec/spec_helper.rb
RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end end
我々はあなたがこの設定を全ての新しいコードで有効にすることを推奨しています。
スコープが変わりました
rspec-mocksのオペレーションは、テストごとのライフサイクルを念頭に設計されました。 これはRSpec 2でドキュメント化されましたが、実行時に常にこれを明示的に強制しているわけではありませんでした、そして我々はときどき、 ユーザーがrspec-mocksの機能をテストごとのライフサイクルの外から使おうと試みたときについてのバグレポートを受け取りました。
RSpec 3では、我々はこれをより厳しくし、このライフサイクルを実行時に明示的に強制します:
- rspec-mocksの機能を
before(:context)
フック(あるいはcurrent exampleが存在しない他のどのcontext)から使う使い方はサポートされません。 - テストダブルは1つのexampleにしか使えなくなりました。もしあなたがテストダブルをそれが作られたexampleの外から使おうとこころみた場合(e.g. うっかりそれをクラスアトリビュートに代入して、それ以降のexampleでそれを使った場合)、あなたはエラーをうけとるでしょう。
あと、我々はあなたに任意の場所で一時的なスコープをつくれるような新しいAPIを提供します(例えばbefore(:context)
フックとか):
my_web_crawler_spec.rb
describe MyWebCrawler do before(:context) do RSpec::Mocks.with_temporary_scope do allow(MyWebCrawler).to receive(:crawl_depth_limit).and_return(5) @crawl_results = MyWebCrawler.perform_crawl_on("http://some-host.com/") end # ブロックが完了すると検証とリセットが起こる end # ... end
これらの変更を実装し、たすけてくれてありがとうSam Phippen、
そしてwith_temporary_scope
の機能を提案してくれてありがとうSebastian Skałacki。
より詳しい情報:
any_instance
のインプリメンテーションブロックがレシーバを渡す
メソッドスタブにインプリメンテーションブロックを提供することは、オブジェクトの状態に寄るような計算を行うのに役にたつでしょう。
RSpec 2でany_instance
を使うとき、残念ながらシンプルにこれを行う方法はありませんでした。
RSpec 3では、レシーバーは最初の引数としてany_instance
のインプリメンテーションブロックにわたってくるので、これを簡単に行うことができます:
any_instance_example.rb
allow_any_instance_of(Employee).to receive(:salary) do |employee, currency| usd_amount = 50_000 + (10_000 * employee.years_worked) currency.from_usd(usd_amount) end employee = Employee.find(23) salary = employee.salary(Currency.find(:CAD))
これを実装してくれてありがとうSam Phippen。
より詳しい情報:
rpsec-rails
ファイルタイプに基づく推測がデフォルトで無効になりました
rspec-railsはspecファイルのファイルシステム上の場所にもとづき自動的にspecにメタデータを追加します。 これは新規ユーザーに混乱をもたらします、そしてこれを好まないベテランのユーザーもいます。 RSpec 3では、この動作を明示的に有効にするようになりました:
spec/spec_helper.rb
RSpec.configure do |config| config.infer_spec_type_from_file_location! end
流行しているチュートリアルではこの動作を前提にしているため、デフォルトで生成される設定ファイルはこれを有効にしたままです。 自動的な推測を使わずに明示的にspecにタグ付けするためには、typeメタデータを設定してください:
things_controller_spec.rb
RSpec.describe ThingsController, type: :controller do # spec/controllers以下に置くのと同じ意味 end
使用できるtypeの違いについては、それぞれのspecの型ごとにドキュメント化されています、たとえばcontroller specsのためのドキュメントなど。
より詳しい情報:
activemodelのサポートが切りだされました
mock_model
とstub_model
がrspec-activemodel-mocks gemに切りだされました。
切り出してくれて、そして新しいgemのメンテナンスを申し出てくれてありがとうThomas Holmes。
webratのサポートがなくなりました
Webratのサポートがとりのぞかれました。変わりにcapybaraを使ってください。
匿名controllerのカイゼン
rspec-railsは長い間、あなたにテスト用の匿名のコントローラを作ることを可能にしてきました。 RSpec 3では、それらはいくつかのカイゼンを施されました:
- デフォルトで、匿名コントローラは
ApplicationController
を継承せず、説明している対象のクラスを継承するようになりました。この動作はinfer_base_class_for_anonymous_controllers
の設定オプションで無効にすることが出来ます。 - 標準的ではないコンテキストでそれを使ったときについて多くのバグフィックスが行われました、たとえば抽象的な親や
ApplicationController
を継承しなかった場合など。もしあなたが過去に匿名コントローラ絡みの問題を経験しているなら、今が再度それを使ってみるいい機会です。
より詳しい情報:
- Documentation - Anonymous controllers
- rspec-rails #893 - Enable infering base class by default
- rspec-rails #905 - Fix anonymous controller route helpers
- rspec-rails #924 - Don’t assume presence of ApplicationController
最後に
いつものように、全てのchangelogsはそれぞれのサブプロジェクトで見ることができます:
RSpec 3は、ここ4年ではじめてのメジャーリリースです。 この大仕事は大勢のコントリビュータによって成されました。 あなたがRSpecをどう使おうとも、我々はあなたが新しい変更を我々以上に気に入ってくれることを願っています。
このブログ記事を書くのを助けてくれてありがとうXavier Shay、Jon Rowe、校正をありがとうSam Phippen、Aaron Kromer
RSpec 3の新機能: コンポーザブルマッチャー
Myron Marston » New in RSpec 3: Composable Matchersの訳です。
あかんところあったらTwitterで@nilp_までお願いします
RSpec 3の、最も大きな新機能が3.0.0.beta2で公開されました: コンポーザブルマッチャ(組み合わせ可能なマッチャー)です。 これにより強力で壊れづらい検証を書けるようになり、新たな可能性が開けました。
例
RSpec 2.xでは、私はこのようなコードを度々書いてきました。
class BackgroundWorker attr_reader :queue def initialize @queue = [] end def enqueue(job_data) queue << job_data.merge(:enqueued_at => Time.now) end end
describe BackgroundWorker do it 'puts enqueued jobs onto the queue in order' do worker = BackgroundWorker.new worker.enqueue(:klass => "Class1", :id => 37) worker.enqueue(:klass => "Class2", :id => 42) expect(worker.queue.size).to eq(2) expect(worker.queue[0]).to include(:klass => "Class1", :id => 37) expect(worker.queue[1]).to include(:klass => "Class2", :id => 42) end end
RSpec 3では、コンポーザブルマッチャーを使うと、マッチャーを引数として(あるいは、データ構造の中にネストさせて)、他のマッチャーに渡すことができ、これにより次のようにシンプルにspecを書けるようになります
describe BackgroundWorker do it 'puts enqueued jobs onto the queue in order' do worker = BackgroundWorker.new worker.enqueue(:klass => "Class1", :id => 37) worker.enqueue(:klass => "Class2", :id => 42) expect(worker.queue).to match [ a_hash_including(:klass => "Class1", :id => 37), a_hash_including(:klass => "Class2", :id => 42) ] end end
我々は次のようなケースでも、失敗時のメッセージの読みやすさを保つため、inspect
の結果ではなく、与えられたマッチャーのdescription
を使うようにしました。
例えば、もし我々がqueue <<
の行をコメントアウトして、このspecでテストしている実装を壊してしまったとき、specは次のように失敗します:
1) BackgroundWorker puts enqueued jobs onto the queue in order Failure/Error: expect(worker.queue).to match [ expected [] to match [(a hash including {:klass => "Class1", :id => 37}), (a hash including {:klass => "Class2", :id => 42})] Diff: @@ -1,3 +1,2 @@ -[(a hash including {:klass => "Class1", :id => 37}), - (a hash including {:klass => "Class2", :id => 42})] +[] # ./spec/background_worker_spec.rb:19:in `block (2 levels) in <top (required)>'
マッチャーエイリアス
もう気づいたかもしれませんが、上記の例ではa_hash_including
をinclude
の代わりに使っていました。
RSpec 3は、同じようなエイリアスをすべてのビルトインマッチャーに与えており、それにより文章としての読みやすさと、よりよい失敗時のメッセージが提供されます。 例えば、この検証と、失敗時のメッセージを比べてみましょう:
x = "a" expect { }.to change { x }.from start_with("a")
expected result to have changed from start with "a", but did not change
x = "a" expect { }.to change { x }.from a_string_starting_with("a")
expected result to have changed from a string starting with "a", but did not change
a_string_starting_with
はstart_with
にくらべより冗長な一方、失敗時のメッセージは本当に読み下しやすく、「日本語でおk」とならずにすみます。
我々は1つあるいはもっと多くのエイリアスをすべてのRSpecのビルトインマッチャーに与えました。
我々は、一環した表現を使うように努めているので(だいたいは "a [オブジェクトの型] [動詞]ing"のかたち)、どんなエイリアスがあるか推測するのは楽です。
このあといくつも例を出すので、あなたはそれを見ることができますし、RSpec 3のドキュメントにはすべてのリストを載せる予定です。
また、(RSpecのビルトインマッチャーであるかカスタムマッチャーであるかにかかわらず)自分自身で手軽にエイリアスを定義できるpublicなAPIがあります。
これはrspec-expectations
の中で、start_with
のエイリアスのa_string_with
を提供するためのコードです:
RSpec::Matchers.alias_matcher :a_string_starting_with, :start_with
Compound Matcher Expressions(マッチャー合成式)
Eloy Espinacoは、新機能として、マッチャーを組み合わせるための別の手段を提供するのに貢献しました: and
とor
によるマッチャーの合成です。
例えば、あなたはこうやって書かずに:
expect(alphabet).to start_with("a") expect(alphabet).to end_with("z")
2つのマッチャーを組み合わせて1つのマッチャーにすることが出来ます。
expect(alphabet).to start_with("a").and end_with("z")
もちろん、or
でも同じことができます。あまり一般的ではないかもしれませんが、これは、正しい値のリストを表現するのにすごく便利です(例えば、検証したい値がはっきりしない場合)
expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
私は、これは特に、Jim Weirichのrspec-givenを使ってInvariantsを表現するのに便利なんじゃないかと考えています。 もちろん、マッチャー合成式を、引数として他のマッチャーに渡すことも出来ます:
expect(["food", "drink"]).to include( a_string_starting_with("f").and ending_with("d") )
注: この例の、ending_with
はend_with
マッチャーのエイリアスです
どのマッチャーが、マッチャー引数をサポートしてる?
RSpec 3では、我々は引数としてマッチャーを受け取れるよう、たくさんのマッチャーを更新しました、しかしすべてというわけではありません。
おおむね、わたしたちが納得できるようなものはすべて更新しました。 マッチャー引数をサポートしないマッチャーの1つに、マッチャー引数をうけつけることが出来ないことが明確なものがあります。
例えば、eq
マッチャーは、actual == expected
の場合にのみ真になります。
そのため、eq
がマッチャー引数をサポートすることはできません。
私は、マッチャー引数をサポートするすべてのビルトインマッチャーのリストを作りました↓
change
change
マッチャーのby
メソッドは、マッチャー引数を受け取れます。
k = 0 expect { k += 1.05 }.to change { k }.by( a_value_within(0.1).of(1.0) )
同様にfrom
とto
にもマッチャー引数を渡すことができます
s = "food" expect { s = "barn" }.to change { s }. from( a_string_matching(/foo/) ). to( a_string_matching(/bar/) )
contain_exactly
contain_exactly
は、match_array
の新しいエイリアスです。
match_array
よりも少し意味が明確です(いま、match
でも同じように配列にマッチすることができます、しかしmatch
の方は順番を気にする必要があります、一方match_array
では気にする必要はありません)
また、配列の要素をそれぞれ、個別の引数として渡すことができます。match_array
のように、1つの配列に詰め込むことを強いらません。
expect(["barn", 2.45]).to contain_exactly( a_value_within(0.1).of(2.5), a_string_starting_with("bar") ) # ...which is the same as: expect(["barn", 2.45]).to match_array([ a_value_within(0.1).of(2.5), a_string_starting_with("bar") ])
include
include
では、コレクションの要素、ハッシュのキー、ハッシュの中のkey/valueペアのサブセットに対するマッチが出来ます。
expect(["barn", 2.45]).to include( a_string_starting_with("bar") ) expect(12 => "twelve", 3 => "three").to include( a_value_between(10, 15) ) expect(:a => "food", :b => "good").to include( :a => a_string_matching(/foo/) )
match
文字列と正規表現、文字列と文字列のマッチに付け加えて、今ではarray/hashのデータ構造に対してもうまく動きます、好きなだけネストしまくっていてもです。 マッチャーは、ネストのどの階層でも使うことができます。
hash = { :a => { :b => ["foo", 5], :c => { :d => 2.05 } } } expect(hash).to match( :a => { :b => a_collection_containing_exactly( an_instance_of(Fixnum), a_string_starting_with("f") ), :c => { :d => (a_value < 3) } } )
raise_error
raise_error
が、例外クラスに対するマッチャーか、例外のメッセージに対するマッチャー、あるいはその両方を受け付けるようになりました。
start_with and end_with
これをみれば一目瞭然:
expect(["barn", "food", 2.45]).to start_with( a_string_matching("bar"), a_string_matching("foo") ) expect(["barn", "food", 2.45]).to end_with( a_string_matching("foo"), a_value < 3 )
throw_symbol
Symbol以外の他の引数に対してマッチするマッチャーをthrow_symbol
に渡せます:
expect { throw :pi, Math::PI }.to throw_symbol(:pi, a_value_within(0.01).of(3.14))
yield_with_args と yield_successive_args
これらのマッチャー(yield_with_args
/yield_successive_args
)の、yield
に渡された引数を明示するのに、マッチャーを使うことができます:
expect { |probe| "food".tap(&probe) }.to yield_with_args( a_string_starting_with("f") ) expect { |probe| [1, 2, 3].each(&probe) }.to yield_successive_args( a_value < 2, 2, a_value > 2 )
まとめ
これは、RSpec 3の新機能の1つです。 私はこの機能に一番わくわくしました、あなたにもこの興奮が伝わるといいなぁと思っています。 この記事が、あなたが必要なことだけを書けばそれ以外のことを書かなくてよくなる(壊れやすいテストを書くのを避ける)手助けになればいいなあと思います。
bundle installを爆速で処理する
Ruby - Bundlerで並列処理??bundle installを爆速で処理する方法。 - Qiita [キータ]
バージョン上がるまで待てば良さそうだけど待つのもいやなのですぐやる。
gem install bundler --pre
rbenv-bundler
のエラーが出る。
とりあえず入れなおしてみる
brew update brew unlink rbenv-bundler brew uninstall rbenv-bundler brew install rbenv-bundler
再度やっても
undefined method `refresh' for #<Bundler::RubygemsIntegration::Future
が出る。 少し追いかけたけど諦めた。
さくらVPSを借りた
sudo apt-get update sudo apt-get upgrade
インストールするときにされてるようでパッケージなし。
などを参考に設定していく
Keyboard Layoutを間違えたかとおもいきや
MacBookに合わせる。
sudo dpkg-reconfigure keyboard-configuration
とりあえずMacBook/MacBook Pro (Intl)
、English (US)
、English (US)
した。それでもおかしい。:
が打てない。viが抜けれない。
どうやらの問題にぶち当たったよう↓
clicklog (さくらのVNCコンソールを英語キーボードで利用する際に発生する問題 さくらのVPSは qemu...)
つらさしかない。諦めた。とっととSSH設定しよう。
sshのポート番号を変える
sudo vi /etc/ssh/sshd_config
TCPやUDPにおけるポート番号の一覧 - Wikipedia
/proc/sys/net/ipv4/ip_local_port_range
見ようにも、_
が打てなくて見れない。つらい。
適当に決める。
ruby -e "puts rand 49152..65535"
- Portをいじる
- PermitRootLoginをnoにする
sudo service ssh restart
sshでログインしようとするが入れない。僕が最初パスワード、記号が入っていて、それが日本語キーボードで入力されたことになっていたからだった、、、つらい。
日本語キーボードで打つとこうなるだろうな、というパスワードを入力すると無事入ることが出来た。すぐにsudo passwd
でパスワードを変更する。
次は公開鍵など。VPSの方で
ssh-keygen
とりあえずこれでVPSに~/.ssh
出来る。VPSの~/.authorized_keys
に普段使ってる鍵を追加するため、ローカルのマシンで
scp -o PubkeyAuthentication=no -P 12345 ~/.ssh/id_rsa.pub username@wwwxxxxxui.sakura.ne.jp:~/.ssh/authorized_keys
んでローカルからパスワードなしでsshで入れるか確認する。
ssh username@wwwxxxxxui.sakura.ne.jp -p 12345
はいれた。/etc/ssh/sshd_config
で
PasswordAuthentication no
にしておく。
sudo service ssh restart
ローカルから公開鍵認証なしで入ろうとしてみる
ssh -o PubkeyAuthentication=no username@wwwxxxxxui.sakura.ne.jp -p 12345
無事はいれない。
ドメイン
さくらのDNS逆引きレコードで設定する。
/etc/hosts
と/etc/hostname
いじった。再起動中。
おわり
とりあえずここまで。
後は
knife solo prepare 〜
でChefを準備して、Chefのレシピを書いて管理する予定。 何を動かそうかな。
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?
(訳者より: 手伝いたい方はきっと英語も読めるだろうと思うのでここは訳しませんლ(╹◡╹ლ)
Ruby5 Episode #384 - July 9, 2013
Ruby5 - Episode #384 - July 9, 2013
Decluttering lib, Puma vs. Passenger, Exception Notification 4.0, Ettu and ETags, Octopus 0.6.0, The self-pipe trick
今回は7分ある。長い感じする。 早くも日本語訳がんばるのあきらめてます。もっと適役いるはず(´・ω・`)
Declutter Lib
Kuba Suderが、彼自身の経験をもとにどうやって散らかったlibディレクトリを綺麗に整理するかかいたブログ記事について。
Declutter your lib directory | Lunar Logic Blog
今のプロジェクトのlibフォルダの中みたらどれぐらいの違う種類のファイルが詰まってる? 「そんなんわかんないよ」「しょうがないよね」
Railsはmodel/view/controllerのための良さげなディレクトリ構造を作ってくれるよね、最近だとassetsとか。
ただRailsが作ってくれるフォルダに収まらないのは全部libいきになっちゃうよね。
Kuba Suderが、自身がlibを綺麗にした経験をもとに、libフォルダの整理方法についてブログ記事をかいた。
いいね、それで僕はどうやったらlibを綺麗にできるんだ?
彼によればappに関係あるすべてものは、libにおかずにapp以下におくべきだ。
なるほど、decoratorとかpresenterとかサービスオブジェクトとかバックグラウンドジョブとか、app以下のサブフォルダにおくってことだね?
そうだ。あと他のクラスにmixinするモジュールはapp/models/concerns、app/models/controllersにいれよう。
おっけ、で、libに置いとくべきなのはどんなファイル?
libに置くべきなのは他のプロジェクトでも再利用出来るような一般的なものだけだ。 例えば外部Webサービスのインターフェイスとか、データベースツールとか、Rubyのコアクラスを拡張するようなものとか、gemとして切り出せるようなものとか。
詳しくはブログをチェックしてね、libを綺麗にするためのベーシックガイドラインがのってるよ。
Puma vs. Passenger
Phusion Passengerのひとが書いたPumaとPassengerの比較記事について。 いくつかの見方、並行性とか、開発モデルとか、CPUバウンド/IOバウンドなアプリでのパフォマンスとか、メモリの使用量とか、管理ツールとか、GCとかデバッグとかフォールトトレランスとか、リソースのコントロールほうとかドキュメントとランタイムサポートとか。
Puma vs Phusion Passenger · FooBarWidget/passenger Wiki
Exception Notification 4.0
Sebastian MartinezがException Notification gemのバージョン4.0をリリースした話。
Rack・Railsアプリで、email/campfireとかで通知してくれるgemだよね?何が変わったの?
バグフィックス、Sidekiqとresqueのサポート、通知エラーのよりよよいログとり、Rails 4との互換性。
いいね! (Rails 4のことクアトロいうと聞き取りづらい)
Email/Campfire/webhook通知出来るようになった。 カスタム通知が作れるようになった。 configuration apiもかわったよ。 詳しくはプロジェクトのドキュメントを見てね。
Ettu generates smarter ETags
Etagsの話、難しいので訳スルー(´・ω・`)
CloudSpaceのひとが出したgem。
デプロイしたあとも、古いHTML/CSS/JavaScriptが表示され続けてる問題を解決するやつっぽい。
コントローラで、レコードをキーにして、fresh_when
使ってる場合レコード更新しないとEtagsも更新されないので、HTML/CSS/JavaScriptが更新されてるけど、fresh_when
に渡してるレコード自体のキーは更新されてないから、Etagsも更新されなくて、Etagsが更新されないからページの内容も変更がないとみなされて、古いページがそのまま表示されちゃうっていう問題があるみたい。
Ettuつかうと、viewとかassetsの変更も考慮してETags生成してくれるっぽい。 使いかたはGemfileに追加してあとはふつーにfresh_when使えばいいみたい。超絶便利...
Et tu, Rails? | Cloudspace | Blog
Octopus 0.6.0 was released
タコうまいよね。そっちのオクトパスの話じゃないよ(w)
Octopus 0.6.0 · tchandy/octopus
ActiveRecordでデータベースのシャーディングを可能にするOctopusのバージョン0.6.0が出た。
(複数のデータベースを1つのアプリで使いたいとき?ほにゃらら?聞き取れなかった)
データベースのシャーディングを実装するプロジェクトいくつかあるよね db-charmerとかdata_fabricとかmulti_dbとか、それぞれのプロジェクトで制限はある。 Octopusのメインゴールはシャーディングするためのよりよい方法を提供することだ。
新しいバージョンは、リファクタリングされてクリーンナップされ、Octopus::Loggerが取り除かれ、MySQLじゃなくてMySQL2でテストするようになった、一番重要なのはRails 2のサポートをやめたこと。 もしRails 2が必要なら、Octopusの0.5の最新版が使えるよ。 0.6はRails 3のサポートのみで、まだリリースされてないけど0.7ではRails 4をサポートする予定。
詳細はプロジェクト参照してね。
The self-pipe trick
Unixシグナルうけとったとき、どうそれを処理するかの話っぽいけど難しいから訳スルー