2
0

More than 1 year has passed since last update.

Rspecでhave_receivedのテストにハマった~subjectはit内で必ず呼び出さなければいけない~

Last updated at Posted at 2021-10-08

はじめに

何回も同じ失敗を繰り返さないように自分を戒めるためのメモ書きです

テストが通らない

テストしたメソッド

user.rb
def user_update
  ~何らかの処理~
  update(params: params)
end

記述したテスト

user_spec.rb
let_it_be(:user) { create(:user) }

describe "#user_update" do
  subject{ user.user_update }
  
  before do
    allow(user).to receive(:update).whith(params: anything)
  end

  it { expect(user).to have_received(:update).with(params: anything).once }
end

実行結果

実行結果
expected: 1 time with arguments: ({:params=>anything})
           received: 0 times

調査

メソッドの最初とupdateの直後にpryして調査したところ、updateは実行されており、allowかitに問題があるのではないかと踏んで、subject!にしたり、beforeをdescribeの上に記述したりしたが、同じエラーが出てしまった

動いたテスト

user_spec.rb
let_it_be(:user) { create(:user) }

describe "#user_update" do
  subject{ user.user_update }
  
  before do
    allow(user).to receive(:update).whith(params: anything)
  end

  it do
   expect(subject).to eq(nil) #subjectを呼び出している
   expect(user).to have_received(:update).with(params: anything).once
  end
end

解説

subjectはit内で呼び出さなければ実行されず、今回のようにテストが通らない
is_expected.toは暗黙的にsubjectを呼び出しているので、subjectを別で呼び出す必要はないが、expect(user)はsubjectを呼び出していないので、別途呼び出す必要がある
もちろん、subjectを呼び出せばいいので、expect(subject)ではなく、subject単体で呼び出したほうが簡潔な記述かもしれない

user_spec.rb
it do
   subject #subjectを呼び出している
   expect(user).to have_received(:update).with(params: anything).once
end

まとめ

  • subject { ◯◯ } は単に定義しているだけで、別途呼び出さないと実行されない
  • is_expected.toは暗黙的にsubjectを呼んでいる
2
0
0

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
2
0