LoginSignup
4
3

More than 5 years have passed since last update.

RSpec 3のyield_receiver_to_any_instance_implementation_blocksオプションについて調べてみた

Last updated at Posted at 2014-03-22

RSpec 3で追加された不思議なオプション

こちらのQiita記事を参考にしながら、「Everyday Rails - RSpecによるRailsテスト入門」サンプルアプリケーションをRSpec 3にアップグレードしていました。

@yujinakayamaさんが作成されたTranspecという便利なgemを使うと、あら不思議、既存のテストコードがRSpec 3対応のものに書き換えられてしまいます。

ただ、diffを確認していたところ、以下のrspec_helper.rbの変更が気になりました。

# spec_helper.rb
config.mock_with :rspec do |c|
  c.yield_receiver_to_any_instance_implementation_blocks = true
end

このオプションは何を意味しているのでしょうか?

開発者ブログになんか書いてある

このオプションに関する説明はRSpec開発者のmyronmarston氏のブログに書いてありました。

ブログ原文

Screen Shot 2014-03-22 at 19.49.35.png

日本語訳
Screen Shot 2014-03-22 at 20.17.37.png

が、この説明を読んでも何かピンと来ない・・・。
というわけで、実際にコードを動かしながら確認してみました。

このオプションの目的はブロック引数にインスタンスを渡すかどうかを決めるだけ

実際に動かしてみると、なんてことはありません。
このオプションは、any_instanceを使っているメソッドでブロックを使ったときに、そのインスタンスをブロック引数として渡すかどうかの違いを決めるためのオプションでした。

上記のブログでも出ている以下のようなコードを例に挙げます。

allow_any_instance_of(User).to receive(:age) do |user|
  ((Date.today - user.birthdate) / 365).floor
end

yield_receiver_to_any_instance_implementation_blockstrueをセットすると、ブロック引数にuserが渡されます。

falseをセットすると、ブロック引数に何も渡されません。(なので、user.birthdateがエラーで落ちます)

RSpec 2.14ではブロック引数に何も渡さないので、RSpec 3で挙動を合わせる必要があればfalseを設定します。
また、RSpec 3ではこのオプションのデフォルト値はtrueになっているので、何も設定しなければuserが渡されるようになっています。

おまけ: このオプションをfalseにする必要がある場合

2014.03.23追記

@yujinakayamaさんが「どっちでもいいわけではない」理由をこの投稿のコメントに書いてくれました!
なので、以下の僕の書いた内容は無視して、@yujinakayamaさんのコメントを参考にしてくださいm(_ _)m

========

個人的な感想を言えば、「別にどっちでもえーやん。ブロック引数を渡したって既存のspecが壊れることは滅多にないでしょうに」と思いました。
なので、このオプションは「常にtrue」でも実害がないと思うのですが、falseにしなければならないケースをムリヤリ考えてみました。

# user.saveは常にtrueを返すようにする
f = ->{ true }
allow_any_instance_of(User).to receive(:save, &f)

falseにしなければ落ちてしまうケースは、上のようなテストコードを書いていた場合です。
ブロックにラムダを渡している場合は引数の数が厳密にチェックされるので、ブロック引数が渡されると引数の数が合わないと怒られてエラーになります。

が、おそらくレアケースだと思いますし、最悪エラーが出たらスペックを書き直せばいいと思うのですが、どうなんでしょう??

まとめ

ブロック引数が渡されるようになった背景は、クラスのインスタンスがないと凝った値を返したりするときに不便、ということみたいです。
確かに、その必要性については僕も十分理解できます。ないより、あった方が絶対便利ですもんね。

というか、any_instanceの存在は知っていましたが、ブロックを使えることは知らなかったので、今回の件で「こんなことができるんだ」と勉強になりました。(^^;)

お知らせ

Everyday Rails - RSpecによるRailsテスト入門

4
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3