RSpec Class Variable Testing
If your class has internal states that you would like to test I find that using the class_variable_get
a nice way of approaching this. This does not require you to expose any of the variables in the class, so the class can stay untouched.
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
I know this is not what you ask for in your question, but it is in the title, which got me here.
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
Or maybe a better way is to reload the class
describe 'items' do
before(:each) do
Object.send(:remove_const, 'Foo')
load 'foo.rb'
end
end
I found this question pursuing a slightly different problem -- clearing a cached class variable between rspec examples.
In a module, I have an expensive class config, which I cache like this:
module Thingamizer
def config
@config ||= compute_config_the_hard_way()
end
end
class Thing
extend Thingamizer
end
In my rspec tests of Thing, compute_config_the_hard_way
was only called the first time. Subsequent calls used the cached version, even if I mock compute_config_the_hard_way
to return different things in other tests.
I resolved this by clearing @config before each example:
before { Thing.instance_variable_set(:@config, nil) }
Now the the thing I was hung up on is that @config is a class variable, not an instance variable. I tried many variations of class_variable_set
without luck.
The wrinkle here is that Thing (the class) is actually an instance of Class. So what seems to be a class variable in a class method is actually an instance variable, in an instance of Class (i.e. Thing). Once I wrapped my head around that idea, using instance_variable_set
instead of class_variable_set
made perfect sense.
See Using Instance Variables in Class Methods - Ruby for a discussion of class variables as instance variables.