我有两节课:
1.Sale是ActiveRecord的子类;它的工作是将销售数据保存到数据库中.
class Sale < ActiveRecord::Base def self.total_for_duration(start_date,end_date) self.count(conditions: {date: start_date..end_date}) end #... end
2.SalesReport是一个标准的Ruby类;它的工作是生成和绘制有关销售的信息.
class SalesReport def initialize(start_date,end_date) @start_date = start_date @end_date = end_date end def sales_in_duration Sale.total_for_duration(@start_date,@end_date) end #... end
因为我想使用TDD并且我希望我的测试运行得非常快,所以我为SalesReport编写了一个不加载Rails的规范:
require_relative "../../app/models/sales_report.rb" class Sale; end # NOTE I have had to re-define Sale because I don't want to # require `sale.rb` because it would then require ActiveRecord. describe SalesReport do describe "sales_in_duration" do it "calls Sale.total_for_duration" do Sale.should_receive(:total_for_duration) SalesReport.new.sales_in_duration end end end
当我运行bundle exec rspec spec / models / report_spec.rb时,此测试有效.
但是,当我运行bundle exec rake spec并且类Sale(TypeError)的错误超类不匹配时,此测试失败.我知道错误正在发生,因为Tap是由sale.rb定义并在规范中内联.
所以我的问题是,如果没有定义该类,有一种方法可以将Stub(或Mock或Double)作为一个类吗?这将允许我删除内联类Sale;结束,这感觉就像一个黑客.
如果没有,如何运行bundle exec rspec或bundle exec rake spec,我如何设置我的测试以使它们正确运行?
最后,我不想使用Spork.谢谢!
解决方法
RSpec最近添加的stub_const专门针对以下情况设计:
describe SalesReport do before { stub_const("Sale",Class.new) } describe "sales_in_duration" do it "calls Sale.total_for_duration" do Sale.should_receive(:total_for_duration) SalesReport.new.sales_in_duration end end end
您可能还想使用rspec-fire来代替Sale来使用测试双精度来自动检查真实Sale类中存在的所有模拟/存根方法在运行带有加载的真实Sale类的测试时(例如,当您运行测试套件时) :
require 'rspec/fire' describe SalesReport do include RSpec::Fire describe "sales_in_duration" do it "calls Sale.total_for_duration" do fire_replaced_class_double("Sale") Sale.should_receive(:total_for_duration) SalesReport.new.sales_in_duration end end end
如果在真正的Sale类上重命名total_for_duration,则在模拟方法时rspec-fire会给出错误,因为它在实际类中不存在.