как мне "ожидать" что-то, что вызывает исключение в rspec?

<p>у меня есть метод, который выполняет некоторые действия на модели cat и в случае неправильного ввода вызывает исключение:</p><pre><code>context "hungry cat" do it { expect { eat(what: nil) }.to raise\_error } end </code></pre><p>что я хочу сделать, это проверить, меняет ли этот метод статус кошек, например:</p><pre><code>context "hungry cat" do it { expect { eat(what: nil) }.to raise\_error } it { expect { eat(what: nil) }.not\_to change(cat, :status) } end </code></pre><p>проблема в том, что, так как <code>eat(what: nil)</code> вызовет исключение, второй <code>it</code> потерпит неудачу, несмотря ни на что. итак, возможно ли игнорировать исключение и проверить некоторые условия?</p><p>я знаю, что можно сделать что-то вроде:</p><pre><code>it do expect do begin eat(what: nil) rescue end end.not\_to change(cat, :status) end </code></pre><p>но это слишком уродливо.</p>

<p>вы можете использовать идиому «rescue nil», чтобы сократить то, что у вас уже есть:</p><pre><code>it { expect { eat(what: nil) rescue nil }.not\_to change(cat, :status) } </code></pre>

<p>вы можете <a href="https://www.relishapp.com/rspec/rspec-expectations/docs/compound-expectations">цепочка</a> положительные утверждения с <code>and</code>. если вы хотите объединить в цепочку отрицательный элемент, rspec 3.1 представил <a href="https://relishapp.com/rspec/rspec-expectations/docs/define-negated-matcher"><code>define\_negated\_matcher</code></a> .</p><p>вы могли бы сделать что-то вроде:</p><pre><code>rspec::matchers.define\_negated\_matcher :avoid\_changing, :change expect { eat(what: nil) } .to raise\_error .and avoid\_changing(cat, :status) </code></pre><p>вдохновленный <a href="https://github.com/rspec/rspec-expectations/issues/664#issuecomment-58134735">этот комментарий</a> .</p>

<p>в rspec 3 вы можете объединить два теста в один. я считаю, что это более читабельно, чем подход <code>rescue nil</code>. </p><p><code>it { expect { eat(what: nil) }.to raise\_error.and not\_to change(cat, :status)}</code></p>

<p>звучит странно, что код <code>eat(what: nil)</code> не выполняется отдельно для каждого из ваших тестов и влияет на другие. я не уверен, но, возможно, переписывание тестов немного или решит проблему, или более точно определит, где проблема (выберите ваш вкус ниже).</p><p>использование <code>should</code>:</p><pre><code>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 </code></pre><p>использование <code>expect</code>:</p><pre><code>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 </code></pre>