Ruby 如何测试单例类?
我正在使用RSpec,希望不止一次地测试一个单例类的构造函数 我该怎么做Ruby 如何测试单例类?,ruby,singleton,rspec,testing,Ruby,Singleton,Rspec,Testing,我正在使用RSpec,希望不止一次地测试一个单例类的构造函数 我该怎么做 向您致意RSpec是否允许您执行测试前操作?因此,您可以向静态类添加另一个方法,该方法清除了构造函数中所做的任何操作。然后在每次测试之前调用它。将它重构为一个可以多次构造的类。这样做的副作用(有些人会说是有益的)是从类中删除单例特性 换一种方式来看:您发现需要多次调用构造函数。为什么类只能构造一个实例?Singleton提供了什么好处?您可以简单地为每个规范创建一个新的并阻塞。将您的规范分解为一个可测试的单元。使用“之前”
向您致意RSpec是否允许您执行测试前操作?因此,您可以向静态类添加另一个方法,该方法清除了构造函数中所做的任何操作。然后在每次测试之前调用它。将它重构为一个可以多次构造的类。这样做的副作用(有些人会说是有益的)是从类中删除单例特性
换一种方式来看:您发现需要多次调用构造函数。为什么类只能构造一个实例?Singleton提供了什么好处?您可以简单地为每个规范创建一个新的
并阻塞。将您的规范分解为一个可测试的单元。使用“之前”和“之后”设置并清除您所做的任何事情
在(:each)
之前和(:each)
之后对每个块执行。查看:
require'singleton'
类人们使用单例的一个原因是“全局变量不好,m'kay?”单例是一个全局变量,被隔离在名称空间中,并且具有延迟实例化。考虑一个真正的全局变量是否可以简化事情,特别是如果你不需要懒惰实例化的时候。 < P> >我看到的一个模式是让单体成为真实类的子类。在生产代码中使用单例版本,但在测试中使用基本(非单例)类
例如:
class MyClass
attr_accessor :some_state
def initialize
@some_state = {}
end
end
class MySingletonClass < MyClass
include Singleton
end
class-MyClass
属性访问器:某些状态
def初始化
@某些状态={}
结束
结束
类MySingletonClass
……但我自己也在寻找更好的方法
我的问题的一部分是,我正在使用JRuby并与Java系统首选项挂钩,这是全局的。剩下的我想我可以重构出来。单例类基本上就是这样做的
# 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
def self.instance
@instance ||= new
end
private_class_method :new
因此,通过使用send调用私有方法new,可以完全绕过备忘录
let(:instance) { GlobalClass.send(:new) }
这种方法的一个很好的好处是,运行测试时不会修改全局状态
可能是更好的方法,来自:
这将创建一个匿名类,该类继承自GlobalClass
,所有类级实例变量都存储在该类中。然后在每次测试后将其丢弃,保持GlobalClass
不变。RSpec有“before”和“after”块。但是由于Singleton模式,构造函数不会被调用两次。构造函数不需要被调用多次,但我希望保持规范清晰-因此我在每个规范中只测试构造函数的一部分。关键是“Singleton.\uu init\uuu(Global)”。这对我来说很重要。谢谢。问题是,这会重置单身。如果您的应用程序的任何其他部分依赖于先前设置的全局状态,则会中断。例如,如果您正在使用注册表模式,那么如何在不中断对使用注册表的组件的测试的情况下测试注册表?这是一个简单、直接的解决方案。谢谢,这非常有用。谢谢!这应该是正确的答案。问题是,在测试运行时,全局状态会被修改。请参阅下面我的答案,以获得更清晰的方法。
let(:instance) { GlobalClass.send(:new) }
let(:instance) { Class.new(GlobalClass).instance }