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::SUITS
和 CardDeck::NUM_CARDS
),除非您手动分配 fake_class::SUITS
和 fake_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
使用案例
我在几种不同的情况下发现它很有用。
- 它提供了一种简单的方法来更改用常量表示的类设置以进行一个测试。过去,我经常定义静态类方法,以便它们可以被存根,即使使用常量更有意义。现在你可以直接使用常量了!
- 当待测试类依赖于协作者的类方法时,它使得依赖注入变得容易(例如,当协作者是无状态的时)。您可以轻松地用测试替身存根协作者的类常量。
- 它使得存根未加载的依赖项变得非常简单。Gary Bernhardt 在Destroy all Software #46中详细讨论了这种情况。他提到了将常量作为存根未加载依赖项的可能方式,但建议不要这样做,因为安全管理此操作的复杂性。现在 rspec-mocks 可以为您做到这一点,它要简单得多,也安全得多。
如果您想知道它的工作原理,请查看github 上的源代码。
[^foot_1]: 实际上,这在rspec-fire 中已经存在了大约四个月。但这并不是一个完整的模拟库……它是建立在 rspec-mocks 之上的,现在该功能已被移植到 rspec-mocks。