RSpec 2.11 中的常量存根

Myron Marston

2012 年 6 月 29 日

在 2.11 版本中,rspec-mocks 获得了一项重要的新功能,据我所知,其他任何 ruby 模拟库都没有提供:常量存根[^foot_1]。

让我们看看 API,然后讨论一些使用案例。

API

主要的 API 是 stub_const

describe "stub_const" do
  it "changes the constant value for the duration of the example" do
    stub_const("Foo::SIZE", 10)
    expect(Foo::SIZE).to eq(10)
  end
end

这适用于已定义和未定义的常量;即使中间常量不存在,您也可以存根 A::B::C::D::E::F。当示例完成时,常量将恢复到原始状态:任何新定义的常量将被取消定义,任何修改的常量将恢复到原始值。

注意,常量名称必须完全限定;不会考虑当前的模块嵌套。

module MyGem
  class SomeClass; end
end

module MyGem
  describe "Something" do
    let(:fake_class) { Class.new }

    it "accidentally stubs the wrong constant" do
      # this stubs ::SomeClass (in the top-level namespace),
      # not MyGem::SomeClass like you probably mean.
      stub_const("SomeClass", fake_class)
    end

    it "stubs the right constant" do
      stub_const("MyGem::SomeClass", fake_class)
    end
  end
end

stub_const 还支持 :transfer_nested_constants 选项。考虑您有嵌套常量的情况

class CardDeck
  SUITS = [:spades, :diamonds, :clubs, :hearts]
  NUM_CARDS = 52
end

stub_const("CardDeck", fake_class) 切断了对嵌套常量的访问(CardDeck::SUITSCardDeck::NUM_CARDS),除非您手动分配 fake_class::SUITSfake_class::NUM_CARDS。提供 :transfer_nested_constants 选项来为您处理此事。

# Default behavior:
fake_class = Class.new
stub_const("CardDeck", fake_class)
CardDeck # => fake_class
CardDeck::SUITS # => raises uninitialized constant error
CardDeck::NUM_CARDS # => raises uninitialized constant error

# `:transfer_nested_constants => true` transfers all nested constants:
stub_const("CardDeck", fake_class, :transfer_nested_constants => true)
CardDeck::SUITS # => [:spades, :diamonds, :clubs, :hearts]
CardDeck::NUM_CARDS # => 52

# Or you can specify a list of constants to transfer:
stub_const("CardDeck", fake_class, :transfer_nested_constants => [:SUITS])
CardDeck::SUITS # => [:spades, :diamonds, :clubs, :hearts]
CardDeck::NUM_CARDS # => raises uninitialized constant error

使用案例

我在几种不同的情况下发现它很有用。

如果您想知道它的工作原理,请查看github 上的源代码

[^foot_1]: 实际上,这在rspec-fire 中已经存在了大约四个月。但这并不是一个完整的模拟库……它是建立在 rspec-mocks 之上的,现在该功能已被移植到 rspec-mocks。