我正在使用RSpec测试gem中的类级实例变量(和setter)。我需要测试以下内容:
- 如果从未使用setter,则提供正确的默认值。
- 可以通过设置器成功更新变量。
显然这里有一个运行顺序问题。如果我使用setter更改值,我就会丢失关于默认值的记忆。我可以在setter测试之前将其保存到一个变量中,然后在结束时重置该值,但这只有在所有setter测试都遵循相同的实践时才能保护我。
测试变量默认值的最佳方法是什么?
下面是一个简单的例子:
class Foo
class << self
attr_accessor :items
end
@items = %w(foo bar baz) # Set the default
...
end
describe Foo do
it "should have a default" do
Foo.items.should eq(%w(foo bar baz))
end
it "should allow items to be added" do
Foo.items << "kittens"
Foo.items.include?("kittens").should eq(true)
end
end
class Foo
DEFAULT_ITEMS = %w(foo bar baz)
class << self
attr_accessor :items
end
@items = DEFAULT_ITEMS
end
describe Foo do
before(:each) { Foo.class_variable_set :@items, Foo::DEFAULT_ITEMS }
it "should have a default" do
Foo.items.should eq(Foo::DEFAULT_ITEMS)
end
it "should allow items to be added" do
Foo.items << "kittens"
Foo.items.include?("kittens").should eq(true)
end
end
或者更好的方法是重新加载
类describe 'items' do
before(:each) do
Object.send(:remove_const, 'Foo')
load 'foo.rb'
end
end
如果你的类有你想测试的内部状态,我发现使用class_variable_get
是一个很好的方法。这并不要求您公开类中的任何变量,因此类可以保持不变。
it 'increases number by one' do
expect(YourClass.class_variable_get(:@@number)).to equal(0)
YourClass.increase_by_one()
expect(YourClass.class_variable_get(:@@number)).to equal(1)
end
我知道这不是你的问题所要求的,但它在标题中,这让我在这里。
我发现这个问题追求一个稍微不同的问题——在rspec示例之间清除缓存的类变量。
在模块中,我有一个昂贵的类配置,我像这样缓存:
module Thingamizer
def config
@config ||= compute_config_the_hard_way()
end
end
class Thing
extend Thingamizer
end
在我对Thing的rspec测试中,compute_config_the_hard_way
只被第一次调用。后续调用使用缓存的版本,即使我模拟compute_config_the_hard_way
在其他测试中返回不同的东西。
我通过在每个示例之前清除@config来解决这个问题:
before { Thing.instance_variable_set(:@config, nil) }
@config是一个类变量,而不是一个实例变量。我试了很多class_variable_set
的变体,都没有运气。
这里的问题是Thing(类)实际上是class的实例。因此,在类方法中似乎是类变量的实际上是一个实例变量,在class(即Thing)的实例中。一旦我明白了这个想法,使用instance_variable_set
而不是class_variable_set
就很有意义了。参见在类方法中使用实例变量- Ruby中讨论类变量作为实例变量