Reputation: 15967
I have an issue that seems to be specific to my test suite.
I have a module that holds some defaults in a constant like so:
module MyModule
DEFAULTS = {
pool: 15
}
def self.options
@options ||= DEFAULTS
end
def self.options=(opts)
@options = opts
end
end
module MyModule
class MyClass
def options
MyModule.options
end
def import_options(opts)
MyModule.options = opts
end
end
end
I allow the program to boot with no options or a user can specify options. If no options are given, we use the defaults but if options are given we use that instead. An example test suite looks like this:
RSpec.describe MyModule::MyClass do
context "with deafults" do
let(:my) { MyModule::MyClass.new }
it 'has a pool of 15' do
expect(my.options[:pool]).to eq 15
end
end
context "imported options" do
let(:my) { MyModule::MyClass.new }
it 'has optional pool size' do
my.import_options(pool: 30)
expect(my.options[:pool]).to eq 30
end
end
end
If those tests run in order, great, everything passes. If it runs in reverse (where the second test goes first), the first test gets a pool size of 30.
I don't have a 'real world' scenario where this would happen, the program boots once and that's it but I'd like to test for this accordingly. Any ideas?
Upvotes: 0
Views: 154
Reputation: 2987
You could always use a before(:each)
before(:each) do
MyModule.options = MyModule::DEFAULTS
end
Side note - maybe a class for the configuration.
Something like:
module MyModule
class Configuration
def initialize
@foo = 'default'
@bar = 'default'
@baz = 'default'
end
def load_from_yaml(path)
# :)
end
attr_accessor :foo, :bar, :baz
end
end
And then you could add something like this:
module MyModule
class << self
attr_accessor :configuration
end
# MyModule.configure do |config|
# config.baz = 123
# end
def self.configure
self.configuration ||= Configuration.new
yield(configuration)
end
end
And finally you will reset the configuration in a more meaningful manner
before(:each) do
MyModule.configuration = MyModule::Configuration.new
end
Upvotes: 0
Reputation: 19879
@options
is a class variable in that module. I'm not sure that is technically the right name for it, but that's how it is acting. As an experiment, print out @options.object_id
right before you access it in self.options
. Then run your tests. You'll see that in both cases it prints out the same id. This is why when your tests are flipped you get 30. @options
is already defined so @options ||= DEFAULTS
is not setting @options
to DEFAULTS
.
$ cat foo.rb
module MyModule
DEFAULTS = {
pool: 15
}
def self.options
puts "options_id: #{@options.object_id}"
@options ||= DEFAULTS
end
def self.options=(opts)
@options = opts
end
end
module MyModule
class MyClass
def options
MyModule.options
end
def import_options(opts)
MyModule.options = opts
end
end
end
puts "pool 30"
my = MyModule::MyClass.new
my.import_options(pool: 30)
my.options[:pool]
puts
puts "defaults"
my = MyModule::MyClass.new
my.options[:pool]
And running it...
$ ruby foo.rb
pool 30
options_id: 70260665635400
defaults
options_id: 70260665635400
Upvotes: 1