我有一个班级和规格。
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
并未真正将active
设置为true
:它只是被模拟所吸收。
我有很多问题。如何进行rspec'cing?我应该如何测试这个呢?我是否正确使用了模拟和存根?
非常感谢任何帮助。
答案 0 :(得分: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
答案 1 :(得分: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
,subject
和context
以标准方式安排测试,并允许rspec做一些巧妙的事情,例如生成类的人性化文档。有关rspec最佳实践的更多信息,请查看betterspecs。