読者です 読者をやめる 読者になる 読者になる

有頂天Ruby

ビールを飲みながらRubyについて書きます。

私が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つがMinitestRailsの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?とタイピングしてそれをtruefalseとアサートするべきだときづいたとき、 私は何をどうやって書くかに悩まされすぎていることに気づきました(もちろんまったく違う方法でこれを書くことも出来るでしょう)。

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スニペットをすごい頼ってます。

f:id:nilp:20140601165400g:plain

6. MinitestはただのRuby

公平を期すためにいうと、RSpecも同じくただのRubyです。 しかし一般的にいえばRSpecは全てにおいて―shared examplesや、テストのセットアップや、設定など―魔法のような方法に見えます。

これらは全て "RSpec way" を持っているようにみえます。

Minitestでは単にRubyを使ってこれを扱います。もしあなたがshared examplesが必要なら、 共有したいテストを含んだモジュールをインクルードすればいいですよね?

私は最初の数日間のMinitestの経験で、何かをするための "正しい" 方法を探しているのに気づきました。 私は、尊敬しているコミュニティの人たちに諭され、それがただのRubyなんだと実感しました。 その考え方は私に、RSpecDSLによる魔法を使うというより、その言語を使ってやりたいこと全てをやっていいんだと気づかせてくれました。

ある意味では、たくさんの魔法は私達の視野を狭めてしまうと思います。 いくつかの素敵なものを使って(悪い使い方もして)、我々は、自分たちが使っているツールが、我々の全ての問題を解決してくれるんだと信じはじめていました。 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_forshared_contextを提供するためmainModuleをモンキーパッチしていました。

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

これを実装してくれてありがとうJon Rowe

より詳しい情報:

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を加えるだけではなく、我々は同様にいくつかの追加のビルトインエイリアスを加えました(describecontext上に):

  • 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をモンキーパッチするのに嫌な気はしません、これはあなたの記法の設定にかかわらず常に利用可能です。

何名かのユーザーはもしこれを使い続けることが出来るなら、このshouldexpect記法をどう関連づけるのかについて戸惑いをみせました。 これはRSpec 3でも利用可能なままです(もう一度いいますがあなたの記法の設定に関係なく)、しかし我々は同様にexpect記法に矛盾しない代替のAPIを追加しました:

post_spec.rb

describe Post do
  it { is_expected.to allow_mass_assignment_of(:title) }
end

is_expectedexpect(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 LindemanSam 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では、あなたは複数のマッチャをandorを使ってつなげて一緒にすることができます:

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") )

コードの表現と失敗時のメッセージの可読性を向上させるため、ほとんどのマッチャーは、それらの式が引数として渡されたとき、きちんと読めるようなエイリアスをもっています。

より詳しい情報:

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) }
  }
)

より詳しい情報:

新しい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. <, <=, >, >=)
  • それはinclusiveexclusive2つのモードを提供します。

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_truebe_false)これはRubyの条件の意味を反映しています: be_truenilfalse以外の全ての値で通ります、そしてbe_falsenilfalseの場合通ります。 RSpec 3では、我々はこれらをより明確な意味になるようbe_truthybe_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).itemshave_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::TestCaseTest::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では、我々はshouldrspec-expectationsのメインの記法にすることをやめました。 我々はこれを反映してマッチャープロトコルをアップデートしました:

  • failure_message_for_shouldはいまfailure_message
  • failure_message_for_should_notはいまfailure_message_when_negated
  • match_for_should (カスタムマッチャDSLmatchエイリアス)には変わりのものは用意されず、とりのぞかれました(単にmatchを使ってください)
  • カスタムマッチャDSLmatch_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-mocks

モンキーパッチしたシンタックスを明示的に有効にせずに使うのはdeprecated

rspec-expectationsと共に、我々は rspec-mocks をゼロモンキーパッチのシンタックスへ向けて進みはじめました。 これらは2.14から導入されました、 RSpec 3では、あなたがオリジナルの記法(e.g. obj.stubobj.should_receive、etc)を明示的に有効にせずに使うとdeprecation warningを出すようになりました(rspec-expectationの新しい記法と同様です)。

これを実装してくれてありがとうSam Phippen

receive_messagesreceive_message_chainのための新しい記法

もともとのモンキーパッチの記法には2.14でリリースされた新しい記法にはかけていた機能がいくつかありました。 我々はRSpec 3でこれに対して2つの新しいAPIを提示します: receive_messagesreceive_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 RoweSam Phippen

doubleエイリアスmockstubを取り除きました

歴史的経緯により、rspec-mocksはテストダブルを作るために3つのメソッドを提供してきました: mockstubdoubleRSpec 3では、我々はstubmockをとりのぞき単なるdoubleを使うことを選択しました、そしてdoubleの上にさらなる機能を体系だてていくことにしました(veryfying doubleみたいな、下の方をみてください)。 もちろん、RSpec 3はdoubleにたいしてmockstubエイリアスを提供しませんが、あなたがこれらを使い続けたいならば、これらのエイリアスを定義するのは簡単です:

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_doubleclass_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_modelstub_modelrspec-activemodel-mocks gemに切りだされました。

切り出してくれて、そして新しいgemのメンテナンスを申し出てくれてありがとうThomas Holmes

webratのサポートがなくなりました

Webratのサポートがとりのぞかれました。変わりにcapybaraを使ってください。

匿名controllerのカイゼン

rspec-railsは長い間、あなたにテスト用の匿名のコントローラを作ることを可能にしてきました。 RSpec 3では、それらはいくつかのカイゼンを施されました:

  • デフォルトで、匿名コントローラはApplicationControllerを継承せず、説明している対象のクラスを継承するようになりました。この動作はinfer_base_class_for_anonymous_controllersの設定オプションで無効にすることが出来ます。
  • 標準的ではないコンテキストでそれを使ったときについて多くのバグフィックスが行われました、たとえば抽象的な親や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_includingincludeの代わりに使っていました。

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_withstart_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は、新機能として、マッチャーを組み合わせるための別の手段を提供するのに貢献しました: andorによるマッチャーの合成です。 例えば、あなたはこうやって書かずに:

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_withend_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) )

同様にfromtoにもマッチャー引数を渡すことができます

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

インストールするときにされてるようでパッケージなし。

さくらVPSを借りてまずしたこと at teppeilog

などを参考に設定していく

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"
  1. Portをいじる
  2. PermitRootLoginをnoにする
  3. 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.14RSpec 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に移動します

前にこれについて書いたので、ここでは長々と書きません。 我々はitsrspec-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).itemshave_at_most(x).itemsのような兄弟マッチャも含む)は、expect(team.players.size).to eq(9)のようにシンプルでうまく動く表現に比べると、必要以上に複雑です。

我々はこれらのマッチャをrspec-expectationsから、外部のgemに移動させようと計画しています。

Core: 明示的なデバッガーのサポートをやめます

RSpecは長い間ruby-debug gemによるデバッガーを有効にするための-d/--debugコマンドラインオプションをサポートしてきました。 しかし、今日ではruby-debugRubyデバッグするのに使うただ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に移動する

長い年月、TextMateRuby開発者が使うもっとも人気のあるエディタでした。 RSpecTextMate向けの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は読みやすい記法を作るため、広範囲にわたってモンキーパッチを行って来ました、describeshared_examples_forshared_contextshouldshould_notshould_receiveshould_not_receive、そして全てのオブジェクトで使えるstubなどです。

2.x系のリリースの最後のいくつかで、我々はRSpecによるモンキーパッチングの量を減らすように働いてきました:

  • rspec-core 2.11では、describeはすべてのオブジェクトに追加されなくなりました、代わりに、トップレベルのmainオブジェクトとModuleに追加されるようになりました(つまり全てのclassとmodule内で使用可能です)。
  • rspec-expectation 2.11では、我々はexpect記法を追加しshould記法を無効にするための設定オプションを提供しました 。これはshouldshould_notを全てのオブジェクトから取り除くものです。

  • rspec-core 2.12では、shared_examples_forshared_contextは、全てのオブジェクトに追加されなくなりました。そしてdescribeのようにそれらはトップレベルのmainオブジェクトとModuleにのみ追加されるようになりました。

  • rspec-mocks 2.14では、我々はrspec-mocksがexpectベースの記法をサポートするようアップデートしました、そしてmockするための古い記法を無効にするオプションを提供しました。これはshould_receiveshould_not_receivestubをすべてのオブジェクトから取り除くものです。

上述したように、3.0で我々はRSpecのモンキーパッチをあてたKernel#debuggerを削除します。 また、我々はmainオブジェクトとModuleのトップレベルDSLメソッド(describeshared_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_shouldfailure_message_for_should_notに依存しており、カスタムマッチャ―APImatch_for_shouldmatch_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もかわったよ。 詳しくはプロジェクトのドキュメントを見てね。

Exception notification

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シグナルうけとったとき、どうそれを処理するかの話っぽいけど難しいから訳スルー

RubySource | The Self-Pipe Trick ExplainedRubySource