6

我有两节课:

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 并且我希望我的测试运行得非常快,所以我为不加载 Rails 的 SalesReport 编写了一个规范:

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但是,当我运行错误时,此测试失败superclass mismatch for class Sale (TypeError)。我知道错误正在发生,因为 Tap 由sale.rb规范定义并内联。

所以我的问题是如果没有定义一个类,有没有办法存根(或模拟或双)一个类? 这将允许我删除 inline class Sale; end,这感觉就像一个黑客。

如果没有,我如何设置我的测试,以便无论我运行bundle exec rspec还是运行它们都能正确运行bundle exec rake spec

如果不是,我编写快速测试的方法是否错误?!

最后,我不想使用Spork。谢谢!

4

2 回答 2

4

一个简单的方法是检查是否已经定义了“销售”

unless defined?(Sale)
  class Sale; end
end

在您的测试中,销售也不必是一个类,所以:

unless defined?(Sale)
  Sale = double('Sale')
end
于 2012-10-23T12:53:35.053 回答
4

最近添加的 RSpecstub_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自动检查真实类中存在的所有模拟/存根方法(例如,当您运行测试套件时):SaleSale

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

如果你total_for_duration在真实Sale类上重命名,当你模拟该方法时,rspec-fire 会给你一个错误,因为它在真实类中不存在。

于 2012-10-23T16:46:25.090 回答