RSpec:如何存根方法和方法链

mar*_*rgo 2 singleton rspec ruby-on-rails-4

我有一个单例类,许多其他类和控制器可以访问该类来确定行为。如何在测试中设置单例值以便测试行为。下面的示例代码中,Setting 是一个 Singleton 类,它由数据库支持并存储应用程序范围的设置,并且管理员可以更改这些设置。Floodgate 是访问设置的类。

class Setting
  def instance
    @setting ||= new
  end
end

class Floodgate
  def self.whitelist
    Setting.instance.flood_gate_whitelist
  end
end
Run Code Online (Sandbox Code Playgroud)

以下是 Floodgate 的一些测试,需要访问设置数据库值。

describe Floodgate do
  let(:setting) { Class.create(Setting).instance }

describe ".whitelist" do
  it "returns a list of values on the Settings floodgate whitelist" do
    expect(Floodgate.whitelist).to eq 'google'
  end
end

describe ".allow_traffic_source?" do
  it "returns true if traffic source is on the white list" do
    expect(Floodgate.allow_traffic_source?('google')).to eq true
  end

  it "returns false if traffic source is not on the white list" do
    expect(Floodgate.allow_traffic_source?('facebook')).to eq false
  end
end
Run Code Online (Sandbox Code Playgroud)

上面的第一个和第二个测试失败,因为Setting.flood_gate_whitelist为nil。在 Floodgate 测试中,我如何设置它以便它持续存在,atm d/b 中没有记录。我尝试将其显式设置如下,当我使用 create 时,错误响应是未定义的方法“create”。

let(:setting) { Class.new(Setting, flood_gate_whitelist: 'google').instance } 
Run Code Online (Sandbox Code Playgroud)

SHS*_*SHS 5

存根被调用的消息链。在你的情况下,一个例子是:

before do
  allow(Setting).
    to receive_message_chain("instance.flood_gate_whitelist").
      and_return("google")
end
Run Code Online (Sandbox Code Playgroud)

现在Setting.instance.flood_gate_whitelist代码中的任何位置都将返回"google".

或者,您可以像这样对实例方法进行存根Setting

before do
  allow_any_instance_of(Setting).
    to receive(:flood_gate_whitelist).
      and_return("google")
end
Run Code Online (Sandbox Code Playgroud)

如果您确定实例化Setting正确,请选择后者。

顺便说一句,与配置相关的变量理想地应放入一个*.yml文件中(例如database.yml,要使用哪个数据库),该文件将根据当前项目环境具有不同的值(在许多情况下,这将消除对存根方法的需要)。