Myron Marston » Notable Changes in RSpec 3の雑な訳です。
誤訳・雑すぎる訳がありましたら、Twitterで@nilp_までご連絡頂けると助かります。
RSpec 3の重要な変更
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-itsgemに切りだされました、これは親切にも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