44

我有一个方法可以对 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)会引发异常,第二个it无论如何都会失败。那么,是否可以忽略异常并检查某些条件?

我知道可以执行以下操作:

it do 
  expect do
    begin
      eat(what: nil)
    rescue
    end
  end.not_to change(cat, :status)
end

但是实在是太丑了。

4

5 回答 5

43

您可以将肯定断言与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)

受此评论启发。

于 2016-11-30T07:42:34.293 回答
41

您可以使用“rescue nil”习语来缩短您已有的内容:

it { expect { eat(what: nil) rescue nil }.not_to change(cat, :status) }
于 2013-06-03T11:21:06.150 回答
9

在 RSpec 3 中,您可以将两个测试链接为一个。我发现这比这种rescue nil方法更具可读性。

it { expect { eat(what: nil) }.to raise_error.and not_to change(cat, :status)}

于 2014-09-30T03:43:29.137 回答
0

听起来很奇怪,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
于 2013-06-01T02:38:13.807 回答
0

您还可以将期望放入期望块中。它仍然有点难看,但它应该可以工作:

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 }
于 2019-08-12T02:12:50.323 回答