はじめに
何回も同じ失敗を繰り返さないように自分を戒めるためのメモ書きです
テストが通らない
テストしたメソッド
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を呼んでいる