问题
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
回答1:
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
回答2:
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.
回答3:
# 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
回答4:
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.
回答5:
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.
回答6:
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?
回答7:
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.
回答8:
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.
来源:https://stackoverflow.com/questions/1909181/how-to-test-a-singleton-class