最近、積読状態だった以下を読んでいます。
RSpec学ぶにはなかなか良い本だと思います。
9章の内容が良かったのでご紹介。
leanpub.com
9. 速くテストを書き、速いテストを書く
aggregate_failures
aggregate_failures を使えば、事前処理が同じようなitをまとめて書くことができます。
it "responds successfully" do sign_in @user get :index aggregate_failures do expect(response).to be_success expect(response).to have_http_status "200" end end
aggregate_failures がない状態だと、どこで落ちたかの判別ができないため、従来は it を分けて書くことが推奨されてきました。
しかし、aggregate_failuresを使えば、途中でエラーとなっても、最後までexpectを実行しどのようなエラーがどこで発生したかが分かりやすくなります。
エラー例)
Failures: 1) ProjectsController#index as an authenticated user responds successfully Got 2 failures from failure aggregation block. # ./spec/controllers/projects_controller_spec.rb:13:in `block (4 levels) in <top (required)>' 1.1) Failure/Error: expect(response).to_not be_success expected `#<ActionDispatch::TestResponse:0x00007fdf6580fce0 @mon_mutex=#<Thread::Mutex:0x00007fdf6580fc68>, @mo...ders:0x00007fdf65829ca8 @req=#<ActionController::TestRequest:0x00007fdf6580fe98 ...>>, @variant=[]>>.success?` to return false, got true # ./spec/controllers/projects_controller_spec.rb:14:in `block (5 levels) in <top (required)>' 1.2) Failure/Error: expect(response).to have_http_status "201" expected the response to have status code 201 but it was 200 # ./spec/controllers/projects_controller_spec.rb:15:in `block (5 levels) in <top (required)>'
こりゃ使わない手はないでしょう。
doubleとinstance_double
doubleとinstance_doubleの違いについて記載があり、わかりやすい例があったのでご紹介。
1対多の関係のこう言うモデルがあったとします。
class User < ApplicationRecord has_many :notes def name [first_name, last_name].join(" ") end end class Note < ApplicationRecord belongs_to :user delegate :name, to: :user, prefix: true end
Noteにuserモデルにdelegateさせるメソッドを生やしていて、これをテストする場合。 通常は、FactoryBotでデータ作成してテストします。
it "delegates name to the user who created it" do user = FactoryBot.create(:user, first_name: "Fake", last_name: "User") note = Note.new(user: user) expect(note.user_name).to eq "Fake User" end
doubleを使うことで、DBへのアクセスを無くしてテストすることができます。
it "delegates name to the user who created it" do user = double("user", name: "Fake User") note = Note.new allow(note).to receive(:user).and_return(user) expect(note.user_name).to eq "Fake User" end
一見良さそうに見えますが、例えば、User#name の名称が full_nameなどに変わったとしても
テストは成功してしまいます。そこで登場するのが、 instance_double
。
it "delegates name to the user who created it" do user = instance_double("User", name: "Fake User") note = Note.new allow(note).to receive(:user).and_return(user) expect(note.user_name).to eq "Fake User" end
これを使うと、Userモデルに定義されているかどうかまで検証してくれます。(第一引数が大文字になっています。)
See Also
aggregate_failuresやinstance_doubleについても自分のブログで少し言及していたことを忘れていました。。
RSpecのベストプラクティスとRSpec3の新機能 - rochefort's blog http://rochefort.hatenablog.com/entry/2016/02/16/080000