【问题标题】:How should I spec this我应该如何指定这个
【发布时间】:2009-10-02 08:32:10
【问题描述】:

以下规范有效,但我知道它不应该是这样的。我很难理解 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 的好资源,请您添加一些链接。

谢谢

【问题讨论】:

    标签: ruby-on-rails rspec


    【解决方案1】:

    我会在这里改变几件事:

    • it 中的大部分代码只是提供上下文,所以它应该在你的 before(:each) 块中。
    • 您正在设置消息期望值,但您似乎并没有真正对此进行测试。我认为期望应该改为stub。另一个测试可能是 it 'should call product_attributes',实际上您将在其中测试该期望 - 我不提倡您这样做,因为您将测试实现而不是行为,而只是说明问题。
    • 您将在该消息期望中返回@product_attributes,并在对 find_by_name 调用进行存根之后立即使用它。但是,您从未定义过@product_attributes。我认为这应该是一个模拟对象,我不确定它在那种情况下真正是什么。也许它是 nil,你正在对它进行一些方法的存根。

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

    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
    

    几个测试资源:

    【讨论】:

    • 谢谢,这很有意义。我复制数据的原因是产品属性会随着时间而改变,但配方的属性需要保持不变。除非用户想要更改它们。
    猜你喜欢
    • 2016-01-26
    • 1970-01-01
    • 2020-08-29
    • 2011-03-09
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    • 2013-07-17
    • 1970-01-01
    相关资源
    最近更新 更多