每个只有一个期望{}阻止:那么如何改进这个规范呢?

时间:2012-07-15 17:03:50

标签: ruby-on-rails testing rspec

My Rails模型:任务has_many位置。

场景:当我创建一个新位置时,它应该创建一个任务。我想测试一下,我这样做:

context "creating a new position" do
  let(:position) { create :position, name: 'Read some books', :task => nil }

  it "should create a simple task" do
    Task.find_by_name('Read some books').should be_nil # First should
    position # Execute let() block (FactoryGirl is lazy evaluating)
    Task.find_by_name('Read some books').should_not be_nil # Second (more relevant) should
  end
end

那么我应该如何改进我的考试呢?第一个“应该”只是确保没有任务,所以我们可以确保创建位置创建任务。但这违反了“只有一个应该阻止”的原则。那么这个怎么样?

context "creating a new position" do
  let(:position) do
    position = create :position, name: 'Read some books', :task => nil
    Task.delete_all
    position
  end

  it "should create a simple task" do
    position # Execute let() block (FactoryGirl is lazy evaluating)
    Task.find_by_name('Read some books').should_not be_nil
  end
end

或者我应该依靠这样一个事实,即不应该有这样的任务(因为一个干净的测试数据库不会有一个)?感谢您的意见。

更新(解决方案)

经过一些研究后,我找到了RSpec的change匹配器:

let(:position) { create :position, name: 'Read some books', :task => nil }

it "should create a simple task" do
  # Thanks to FactoryGirl's lazy evaluation of let(), the position doesn't yet exist in the first place, and then after calling position in the expect{} block, it is created.
  expect { position }.to change{ Task.count(conditions: { name: 'Read some books' }) }.by(1)
end

2 个答案:

答案 0 :(得分:1)

测试什么

我不会详细说明测试本身是否对任何程度都有用。对我来说,他们似乎在运用基本的数据库功能而不是应用程序逻辑,这是一种边际效用,但只有你才能真正决定测试的重要性。

具体

在你给出的例子中,没有真正的理由使用let块来记忆变量。如果只有一个测试需要记录,则仅在该特定测试中实例化它。例如:

context 'creating a new position' do
  it 'should be nil when the position record is missing' do
    Task.find_by_name('Read some books').should be_nil
  end

  it 'should successfully create a position' do
    create :position, name: 'Read some books', :task => nil
    Task.find_by_name('Read some books').should_not be_nil
  end
end

或者,如果您尝试测试应用程序在缺少记录时的行为方式,那么请继续记住变量或在之前的块中创建记录,但明确删除该记录中的记录测试

多重上下文

最后,如果您发现在个别测试中设置的状态太多,那通常就是您应该考虑将测试分成不同的上下文的线索。例如,您可能希望将测试分成一个上下文,该上下文在记录不存在时检查行为,并且在记录 do 时存在单独的上下文。

就像所有测试一样,它不仅仅是一门科学而是艺术。您的里程可能会有所不同。

答案 1 :(得分:0)

RSpec 2.11允许您将块传递给change,并且它期望块的返回值是变化的。我希望这对你有用:

expect { position }.to change { Task.where(:name => 'Read some books').count }.from(0).to(1)