我该怎么说这个

时间:2009-10-02 08:32:11

标签: ruby-on-rails rspec

以下规范有效,但我知道它不应该是这样的。我很难了解rspec,特别是模拟和存根。

这是型号代码

class RecipeFermentable < ActiveRecord::Base
  belongs_to :recipe
  belongs_to :product

  def set_attributes()
    attrs = product.product_attributes
    self.ppg = attrs.find_by_name(:ppg.to_s).value
    self.ecb = attrs.find_by_name(:ecb.to_s).value
  end
end

这是我写的规范

  it "should set the attributes from the product" do
    @product_attribute1 = mock_model(ProductAttribute, :name => :ppg, :value => 40)
    @product_attribute2 = mock_model(ProductAttribute, :name => :ecb, :value => 1)

    @product = Product.new
    @product.product_attributes << @product_attribute1
    @product.product_attributes << @product_attribute2
    @recipe_fermentable = RecipeFermentable.new
    @recipe_fermentable.product.should_receive(:product_attributes).and_return(@product_attributes)
    @product_attributes.stub(:find_by_name).with(:ppg.to_s).and_return(@product_attribute1)
    @product_attributes.stub(:find_by_name).with(:ecb.to_s).and_return(@product_attribute2)

    @recipe_fermentable.set_attributes

    @recipe_fermentable.ppg.should eql(40)
    @recipe_fermentable.ecb.should eql(1)
 end

首先,我的规格比我的方法更大,而且我使用的是真正的产品。关于为此编写一个更好的规范的方法的一些指示将是非常有帮助的。此外,如果有人知道使用模拟和存根学习rspec的良好资源,请添加一些链接。

由于

1 个答案:

答案 0 :(得分:1)

我会在这里改变一些事情:

  • it中的大部分代码只是提供上下文,因此应该在您的before(:each)块中。
  • 您正在设置消息预期,但看起来并不像您正在测试它。我认为应该将期望转换为stub。另一个测试可能是it 'should call product_attributes',你实际上会测试那个期望 - 我不是在鼓励你这样做,因为你会测试实现而不是行为,只是说明问题。
  • 您将在该消息期望中返回@product_attributes,并在之后使用它来存根find_by_name调用。但是,您从未定义过@product_attributes。我认为应该是一个模拟对象,我不确定真正在该上下文中是什么。也许它是零,你在它上面的几个方法。

有了这两个变化,我们就在这里:

before(:each) do
  @product                = mock_model(Product)
  @product_attribute_ppg  = mock_model(ProductAttribute, :name => :ppg, :value => 40)
  @product_attribute_ecb  = mock_model(ProductAttribute, :name => :ecb, :value => 1)
  @product_attributes     = mock('product_attributes')
  @product_attributes.stub!(:find_by_name).with(:ppg.to_s).and_return(@product_attribute_ppg)
  @product_attributes.stub!(:find_by_name).with(:ecb.to_s).and_return(@product_attribute_ecb)
  @product.stub!(:product_attributes).and_return(@product_attributes)

  @recipe_fermentable = RecipeFermentable.new
  @recipe_fermentable.stub!(:product).and_return(@product)
end

it 'should set the attributes from the product' do
  @recipe_fermentable.set_attributes
  @recipe_fermentable.ppg.should eql(40)
  @recipe_fermentable.ecb.should eql(1)
end

所有这一切,我不完全同意你的方法。我认为你正在重复数据并远离数据库规范化。除非有一个真正的原因(可能是你前进的道路,出于性能原因你必须这样做),我建议如下:

class RecipeFermentable < ActiveRecord::Base
  def ppg
    #rescue nil here so that if attributes is nil, or find_by_name('ppg') is nil, things don't blow up
    product.attributes.find_by_name('ppg').value rescue nil
  end

  #other
end

一些测试资源: