ore*_*256 11 ruby unit-testing rspec ruby-on-rails
我正在为一个接触多个模型的服务对象编写Rspec测试,但我觉得我的测试过于依赖于方法的内部,因此不是很有意义.这是一个例子:
class MealServicer
def self.serve_meal(meal, customer)
meal.update_attributes(status: "served", customer_id: customer.id)
order = customer.order
OrderServicer.add_meal_to_order(meal, order)
CRM.update_customer_record(customer) // external API call
end
end
Run Code Online (Sandbox Code Playgroud)
我想使用双打/存根来模拟行为而不实际保存任何东西到测试数据库(为了性能).但是如果我创建响应消息的双精度数,那么感觉就像我正在测试serve_meal()方法的一个特定实现,并且这个测试太过耦合到那个特定的实现.例如,我需要确保我的customerdouble响应order并返回一个order存根.基本上,当一切都只是一个双重的时候我必须通过确保双精度返回其他双精度来明确说明所有依赖关系,感觉测试最终变得毫无意义.看这里:
it "has a working serve_meal method" do
meal = double(:meal)
customer = double(:customer)
order = double(:order)
allow(customer).to_receive(:order).and_return(order)
allow(OrderServicer).to_receive(:add_meal_to_order).and_return(true)
allow(CRM).to_receive(:update_customer_record).and_return(true)
expect(meal).to receive(:update_attributes).once
expect(OrderServicer).to receive(:add_meal_to_order).once
expect(CRM).to receive(:update_customer_record).once
end
Run Code Online (Sandbox Code Playgroud)
除了实例化实际的膳食,客户和订单对象(可能保存到数据库)之外,还有其他方法可以彻底而有意义地进行测试,然后检查MealServicer.serve_meal(...)是否将对象属性更新为预期?这将最终保存到数据库,因为update_attributes执行保存调用,因此我打算在我的Service对象方法中包含几个方法.
最后因为测试依赖于实现,我不能在方法之前编写测试,这是TDD倡导者所推荐的.这只是感觉倒退.有关编写高效但有用的测试的建议吗?
zet*_*tic 19
这是马丁福勒的Mocks Are Not Stubs中提到的"Mockist vs Classicist"困境.在整个过程中使用模拟(双打)必然需要在协作者上隐藏其他方法并公开实现.这是你为模拟的速度和灵活性付出的代价的一部分.
另一个问题是规范没有自然的"主题",因为这是一种类方法.最终会得到三个需要更新的对象; 从某种意义上说,它们是交替的主体和合作者,取决于正在进行的期望.您可以通过为每个示例设置一个期望来使此更清晰:
describe MealServicer do
context ".serve_meal" do
let(:order) { double(:order) }
let(:meal) { double(:meal) }
let(:customer) { double(:customer, id: 123, order: order }
it "updates the meal" do
allow(OrderServicer).to_receive(:add_meal_to_order)
allow(CRM).to_receive(:update_customer_record)
expect(meal).to receive(:update_attributes).with(status: "served", customer_id: 123)
MealServicer.serve_meal(meal, customer)
end
it "adds the meal to the order" do
allow(meal).to receive(:update_attributes)
allow(CRM).to_receive(:update_customer_record)
expect(OrderServicer).to receive(:add_meal_to_order).with(meal, order)
MealServicer.serve_meal(meal, customer)
end
it "updates the customer record" do
allow(meal).to receive(:update_attributes)
allow(OrderServicer).to_receive(:add_meal_to_order)
expect(CRM).to receive(:update_customer_record).with(customer)
MealServicer.serve_meal(meal, customer)
end
end
end
Run Code Online (Sandbox Code Playgroud)
现在存根总是依赖关系,期望是被测试的东西,这澄清了规范的意图.
因为测试依赖于实现,我不能在方法之前编写测试
我不同意.如果您将期望分开,那么您可以先测试并编写代码以使测试通过,如果您一次只处理一个示例.
编辑
另见Myron Marston撰写的这篇博客文章
| 归档时间: |
|
| 查看次数: |
13241 次 |
| 最近记录: |