16

这是我的 rspec 测试:

it "can release an idea" do
  james.claim(si_title)
  james.release(si_title)
  james.ideas.size.should eq 0
  si_title.status.should eq "available"
end

最后的两条should线真的是个坏主意吗?我在某处读到,您应该每个it块只测试一件事,但是为了确保标题状态发生变化而进行整个测试似乎很愚蠢(相同的函数在我的代码中完成了这两件事)。

4

4 回答 4

23

我对此的解释并不是说每个规范应该只有一个断言/调用should,而是每个规范应该只测试一点行为,例如

it 'should do foo and bar' do
  subject.do_foo.should be_true
  subject.do_bar.should be_true
end

不好-您同时指定了两种不同的行为。

另一方面,如果您的 2 个断言只是验证一件事的不同方面,那么我可以接受,例如

it 'should return a prime integer' do
  result = subject.do_x
  result.should be_a(Integer)
  result.foo.should be_prime
end

对我来说,让一个规范检查它是否返回一个整数和一个单独的规范检查它是否返回一个素数并没有太大意义。

当然,在这种情况下,be_prime 匹配器可以轻松地完成这两项检查 - 也许一个好的经验法则是,如果您可以使用自定义匹配器将多个断言合理地减少到 1,则多个断言是可以的(实际上这样做是否真的值得取决于你的情况)

在您的特定情况下,可以说有两种行为在起作用 - 一种是改变状态,另一种是改变ideas集合。我会改写您的规格以说明发布方法应该做什么-

it 'should change the status to available'
it 'should remove the idea from the claimants ideas'

目前,这些事情总是同时发生,但我认为它们是独立的行为——你可以很容易地想象一个系统,多个人可以声明/发布一个想法,并且只有当最后一个人发布这个想法时状态才会改变。

于 2012-12-28T12:45:51.460 回答
3

我有同样的问题......它应该是每个人都应该这样做(我的老板说),因为它使测试很耗时,而且,正如你所说,很愚蠢。测试需要良好的感觉和灵活性,否则它们最终可能会奴役你。无论如何,我同意你的测试。

于 2012-12-28T11:58:37.487 回答
2

我认为 Frederick Cheung 给出了一个很好的答案(+1),尤其是为什么,但我也想给你一个比较代码,让你看看哪些使用its,lets和语法:beforecontext

context "Given a si_title" do
  let(:james) { James.new } # or whatever
  before do
    james.claim(si_title)
    james.release(si_title)
  end
  context "That is valid" do
    let(:si_title) { Si_title.new } # or whatever

    describe "James' ideas" do
      subject { james.ideas }
      its(:size) { should == 0 }
      its(:whatever) { should == "Whatever" }
    end
    describe "Si title" do
      subject { si_title }
      its(:status) { should == "available" }
    end
  end
  context "That is invalid" do
    # stuff here
  end
end

我什至会更进一步,将期望值设为lets,然后将示例shared_example设为 s,这样它们就可以用来检查不同的方面(空参数、无效参数、不正确的对象……),但我相信这是一个更好的方式来说明你的意图,并且仍然减少任何重复。以弗雷德里克的回答为例:

it 'should return a prime integer' do
  result = subject.do_x
  result.should be_a(Integer)
  result.foo.should be_prime
end

使用 RSpec 的语法来充分发挥你的作用:

  let(:result) { 1 }
  subject{ result }
  its(:do_x) { should be_a(Integer) }
  its(:foo) { should be_prime }

这意味着您可以检查一个主题的多个方面。

于 2012-12-28T15:47:26.907 回答
2

我的策略是始终将多个断言视为潜在问题的标志,值得重新考虑,但不一定是错误的。由于某种原因,我编写的规范中可能有 1/3 最终在其中包含多个断言。

拥有多个断言的问题之一是,当一个断言失败时,您看不到另一个断言是否通过。有时可以通过构建结果数组并声明数组的值来解决此问题。

在您询问的情况下,我认为多个断言不是问题,但我确实看到了其他对我来说似乎是问题的东西。看起来您的规范可能过度耦合。

我认为你试图断言任何事物的行为james,但测试也取决于行为,si_title以便告诉我们对它做了什么(通过它的最终#status价值)。我通常会做的是做si_title一个测试替身并#should_receive用来直接指定它应该期望的消息。

于 2012-12-28T13:21:24.593 回答