鉴于我有一个抽象类,它为子类提供了继承的功能:
class Superclass
class_attribute :_configuration_parameter
def self.configuration_parameter config
self._configuration_parameter = config
end
def results
unless @queried
execute
@queried = true
end
@results
end
private
# Execute uses the class instance config
def execute
@rows = DataSource.fetch self.class._configuration_parameter
@results = Results.new @rows, count
post_process
end
def post_process
@results.each do |row|
# mutate results
end
end
end
这可能由子类使用:
class Subclass < Superclass
configuration_parameter :foo
def subclass_method
end
end
我很难编写RSpec来测试继承和配置的功能而不会滥用全局命名空间:
RSpec.describe Superclass do
let(:config_parameter) { :bar }
let(:test_subclass) do
# this feels like an anti-pattern, but the Class.new block scope
# doesn't contain config_parameter from the Rspec describe
$config_parameter = config_parameter
Class.new(Superclass) do
configuration_parameter $config_parameter
end
end
let(:test_instance) do
test_subclass.new
end
describe 'config parameter' do
it 'sets the class attribute' do
expect(test_subclass._configuration_parameter).to be(config_parameter)
end
end
describe 'execute' do
it 'fetches the data from the right place' do
expect(DataSource).to receive(:fetch).with(config_parameter)
instance.results
end
end
end
我在这里嘲笑的真实世界超类还有一些配置参数和其他一些功能,可以很好地测试这种模式。
我是否遗漏了关于课程或考试设计的明显不好的事情?
由于
答案 0 :(得分:1)
我将跳转到你问题中最具体的部分,关于如何避免使用全局变量将本地参数传递给规范中实例化的虚拟类。
这是您的规范代码:
let(:test_subclass) do
# this feels like an anti-pattern, but the Class.new block scope
# doesn't contain config_parameter from the Rspec describe
$config_parameter = config_parameter
Class.new(Superclass) do
configuration_parameter $config_parameter
end
end
如果您使用Class.new
返回的值,则可以使用本地值调用configuration_parameter
,并避免使用全局值。使用tap
只需对现有代码进行微小更改即可完成此操作:
let(:test_subclass) do
Class.new(SuperClass).tap do |klass|
klass.configuration_parameter config_parameter
end
end
关于如何测试从超类继承的功能的更一般的问题,我认为创建存根子类和为该子类编写规范的一般方法很好。我个人会将您的_configuration_parameter
类属性设为私有,而不是测试configuration_parameter
方法实际设置的值,而是专注于检查值不同来自超类的值。但我不确定这个问题的范围是什么。