I am using RSpec and want to test the constructor of a Singleton class more than one time.
How can I do this?
Best regards
I am using RSpec and want to test the constructor of a Singleton class more than one time.
How can I do this?
Best regards
have a look at http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby:
require 'singleton'
class <<Singleton
def included_with_reset(klass)
included_without_reset(klass)
class <<klass
def reset_instance
Singleton.send :__init__, self
self
end
end
end
alias_method :included_without_reset, :included
alias_method :included, :included_with_reset
end
Singleton classes essentially do this
def self.instance
@instance ||= new
end
private_class_method :new
So you can bypass the memoization altogether by calling the private method new using send
let(:instance) { GlobalClass.send(:new) }
A nice benefit of this way is that no global state is modified as a result of your tests running.
Probably a better way, from this answer:
let(:instance) { Class.new(GlobalClass).instance }
This creates an anonymous class which inherits from GlobalClass
, which all class-level instance variables are stored in. This is then thrown away after each test, leaving GlobalClass
untouched.
# singleton_spec.rb
require "singleton"
class Global
include Singleton
def initialize
puts "Initializing"
end
end
describe Global do
before do
Singleton.__init__(Global)
end
it "test1" do
Global.instance
end
it "test2" do
Global.instance
end
end
% rspec singleton_spec.rb -fd
Global
Initializing
test1
Initializing
test2
One pattern I've seen is having the singleton be a sub-class of the real class. You use the Singleton version in production code, but the base (non-singleton) class for testing.
Example:
class MyClass
attr_accessor :some_state
def initialize
@some_state = {}
end
end
class MySingletonClass < MyClass
include Singleton
end
...but I'm looking for a better way myself.
Part of my problem is that I'm using JRuby and hooking into the Java System Preferences, which are global. The rest I think I can refactor out.
One reason people use singletons because "global variables are bad, m'kay?" A singleton is a global variable, sequestered in a name space, and with lazy instantiation. Consider whether a true global variable might simplify things, especially if you don't need lazy instantiation.
Refactor it into a class that can be constructed multiple times. This has the side-effect (some would say benefit) of removing the Singleton nature from the class.
Look at it another way: you've found a need to call the constructor more than once. Why should the class only construct one instance? What benefit is Singleton providing?
Does RSpec allow you do perform pre-test actions? Is so, you could add another method to you static class that cleared anything done during the constructor. Then just call that prior to each and every test.
You can simply make a new it
and block for each spec. Break down your spec to a testable unit. Use "before" and "after" to set up and clears up anything you did.
before(:each)
and after(:each)
are executed for every it
block.