Catモデルでいくつかのアクションを実行するメソッドがあり、入力が正しくない場合に例外が発生します。
_context "hungry cat" do
it { expect { eat(what: nil) }.to raise_error }
end
_
私がしたいのは、このメソッドが猫のステータスを変更するかどうかを確認することです:
_context "hungry cat" do
it { expect { eat(what: nil) }.to raise_error }
it { expect { eat(what: nil) }.not_to change(cat, :status) }
end
_
問題は、eat(what: nil)
が例外を発生させるため、2番目のit
は何があっても失敗することです。それで、例外を無視していくつかの条件をチェックすることは可能ですか?
私は次のようなことが可能であることを知っています:
_it do
expect do
begin
eat(what: nil)
rescue
end
end.not_to change(cat, :status)
end
_
しかし、それはあまりにも醜いです。
「rescue nil」イディオムを使用して、すでに持っているものを短くすることができます。
it { expect { eat(what: nil) rescue nil }.not_to change(cat, :status) }
chain 肯定的なアサーションはand
でできます。チェーンで否定されたものを混ぜたい場合、RSpec 3.1は define_negated_matcher
。
あなたは次のようなことをすることができます:
RSpec::Matchers.define_negated_matcher :avoid_changing, :change
expect { eat(what: nil) }
.to raise_error
.and avoid_changing(cat, :status)
このコメント に触発されました。
RSpec 3では、2つのテストを1つにチェーンできます。これは_rescue nil
_アプローチよりも読みやすいと思います。
it { expect { eat(what: nil) }.to raise_error.and not_to change(cat, :status)}
期待ブロック内に期待を置くこともできます。それはまだ少し醜いですが、うまくいくはずです:
expect do
# including the error class is just good practice
expect { cat.eat(what: nil) }.to raise_error(ErrorClass)
end.not_to change { cat.status }
eat(what: nil)
コードが各テストに対して個別に実行されておらず、他のテストに影響しているのは奇妙に聞こえます。確かではありませんが、テストを少し書き直すだけで問題が解決するか、問題の場所をより正確に特定できます(以下のフレーバーを選択してください)。
should
の使用:
context "hungry cat" do
context "when not given anything to eat" do
subject { -> { eat(what: nil) } }
it { should raise_error }
it { should_not change(cat, :status) }
end
end
expect
の使用:
context "hungry cat" do
context "when not given anything to eat" do
let(:eating_nothing) { -> { eat(what: nil) } }
it "raises an error" do
expect(eating_nothing).to raise_error
end
it "doesn't change cat's status" do
expect(eating_nothing).to_not change(cat, :status)
end
end
end