4

我有一个类和一个规范。

class Store
  def activate(product_klass, product_id)
    product = product_klass.find(product_id)
    if product.inactive?
      product.update_attribute :active, true
    end
  end
end

describe Store do
  it "should activate an inactive product" do
    product = mock
    product.stub(:inactive?).and_return(true)    
    store = Store.new
    store.activate(22) # 
    product.should be_active
  end
end

运行规范失败。我得到:

Mock received unexpected message :find_by_id with (1)

为了满足这一点,我 product.should_receive(:find_by_id).with(1).and_return(product)在行前添加store.activate(product, 22). (这似乎是错误的做法,因为我不希望我的测试对我正在测试的方法的内部了解太多)

再次运行规范,我得到了失败,下面的行返回false而不是预期的true

product.should be_active

所以,它返回false是因为product.update_attribute :active, true并没有真正设置activetrue:它只是被模拟吸收了。

我有很多问题。如何进行rspec'cing?我应该如何测试呢?我是否正确使用了模拟和存根?

任何帮助深表感谢。

4

2 回答 2

2

我认为激活逻辑根本不属于Store。如果它在 中声明Product,测试对我来说看起来会更自然:

class Product < ActiveRecord::Base
  def activate
    if inactive?
      update_attribute :active, true
    end
  end
end

describe Product do
  it "should activate an inactive product" do
    product = Product.new
    product.activate 
    product.should be_active
  end
end

从那里你可以像这样重写你的Store方法:

class Store
  def activate(product_klass, product_id)
    product = product_klass.find(product_id)
    product.activate
  end
end

describe Store do
  it "should activate an inactive product" do
    product = mock
    product.should_receive(:activate)
    product_klass = mock
    product_klass.should_receive(:find).with(22).and_return(product)
    store = Store.new
    store.activate(product_klass, 22)
  end
end
于 2013-05-22T05:44:55.017 回答
2

我同意@padde 关于产品激活应该在Product模型上的事实,如下所示:

class Product < ActiveRecord::Base
  def activate
    if inactive?
      update_attribute :active, true
    end
  end
end

但是,我会重构测试以使其符合 Rspec 标准实践:

describe Product do
  context "after activating" do   # Human readable situation of the test
    let(:product) { Product.new.activate }
    subject { product }           # Make product the subject of the test

    it { should be_active }       # Product should be active
  end
end

Store测试:

describe Store do
  context "when activating a product" do
    let(:product)       { mock }
    let(:store)         { Store.new }

    before do
      product_klass = double                 # Stub the product class, don't mock
      product_klass.stub(:find) { product }  # We want to test product here, not the class
      store.activate(product_klass, 22)
    end

    subject { product }                      # product is the subject again

    it { should_receive(:activate) }         # it should receive the activate message
  end
end

我取消了对 的期望product_klass,因为在这种情况下,这并不是您真正感兴趣的测试。您可能喜欢将其作为单独的测试。

使用let,subjectcontext以标准方式安排您的测试,并允许 rspec 做一些简洁的事情,例如为您的类生成人性化的文档。有关 rspec 最佳实践的更多信息,请查看betterspecs

于 2013-05-22T09:35:26.627 回答