【发布时间】:2014-07-05 16:49:52
【问题描述】:
我正在为涉及多个模型的服务对象编写 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
我想使用双精度/存根来模拟行为,而不实际将任何内容保存到测试数据库中(以提高性能)。但是,如果我创建了响应消息的替身,那么感觉就像我正在测试一个特定的 serve_meal() 方法的实现,而这个测试与那个特定的实现太耦合了。例如,我需要确保我的 customer double 响应 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
除了实例化正确连接的实际用餐、客户和订单对象(并可能保存到数据库中)之外,还有其他方法可以彻底和有意义地测试,然后检查 MealServicer.serve_meal(...) 是否更新了对象属性是否符合预期?这最终会保存到数据库中,因为 update_attributes 会进行保存调用,而我打算在我的 Service 对象方法中包含的几个方法也是如此。
最后因为测试依赖于实现,我不能在方法之前写测试,这是TDD倡导者推荐的。这只是感觉倒退。关于编写高性能但有用的测试有什么建议吗?
【问题讨论】:
-
是的,您基本上是在对大部分实际代码进行存根,因此除非您实际从中删除行,否则该方法不会真正失败。鉴于对象的性质,我会在集成级别而不是单元测试更多地测试这种业务逻辑。您正在处理做不同事情的多个对象。真正阅读您的方法,这听起来像是一个功能规范。我很想知道您如何在应用程序中使用 MealServicer。但是不要担心使用那些实际的对象。为调用该方法时预期发生的情况编写测试
标签: ruby-on-rails ruby unit-testing rspec