我正在努力保持我的规格清洁和干燥,但我对API的测试除了测试的API版本之外都是相同的。我可以简单地使用这样的东西重复规范:
%w( v1 v2 ).each do |version|
describe "Query #{version} API" do
it "responds with JSON"
# make the call using the version
end
end
end
但是我想要一些更清洁的东西,所以我写了这个方法:
module RepetitivelyDescribe
def repetitively_describe(*args, &example_group_block)
options = args.extract_options!
options.delete(:for).each do |item|
item_args = args.collect(&:dup) + [options.dup]
item_args[0] << " [#{item}]"
describe(*item_args) do
example_group_block.call item
end
end
end
end
RSpec::Core::ExampleGroup.extend RepetitivelyDescribe
然后我的测试看起来更像是这样:
repetitively_describe "Query API", :for => %( v1 v2 ) do |version|
it "responds with JSON"
# make the call using the version
end
end
我意识到这有点迂腐,但它的缩进程度要低一些,如果我打算多打这个电话,我想让它变得更干净。
但当然,它并不像我想的那样有效。我的describe
中对repetitively_describe
的调用未记录到RSpec输出(使用文档格式输出时),尽管其中的示例会重复并按预期使用版本块参数。从本质上讲,该上下文级别会丢失(保留describe
块外部和内部的repetitively_describe
块。
如果需要,a gist中有更详细的示例代码。关于为什么这不正常工作的任何线索?
答案 0 :(得分:5)
所以(道歉,如果我重复你已经知道的东西),但每次调用describe / context时,rspec都会创建一个新类,它是当前示例组类的子类(最终是{{1}的子类})然后使用RSpec::Core::ExampleGroup
来评估该类上下文中的块。如果我跑
module_eval
然后输出
describe "foo" do
puts "#{self}; #{self.superclass}"
describe "behaviour 1" do
puts "#{self}; #{self.superclass}"
context "with x" do
puts "#{self}; #{self.superclass}"
end
end
end
当您调用#<Class:0x007fb772bfbc70>; RSpec::Core::ExampleGroup
#<Class:0x007fb772bfb180>; #<Class:0x007fb772bfbc70>
#<Class:0x007fb772bfa5f0>; #<Class:0x007fb772bfb180>
时,rspec创建一个it
对象并将其附加到self上的类实例变量(当前示例组)。 rspec还会在示例的元数据中粘贴当前示例组,向上走这个示例组树就可以获得示例的完整描述。
您的Example
方法会调用repetitively_describe
,因此,当您调用describe
self时,确实是新创建的示例组。当proc被评估时,它当然会记住调用它时example_group_block.call item
的值是什么,所以你对self
的调用是针对重复描述时最新的示例组(通过洒一些可以轻松验证)要求在整个代码中检查self的值。类似地,对describe的调用会将示例组添加为外部示例组的子项,而不是it
创建的子项。
您当然需要做的是致电repetitively_describe
,保留正确的自我价值。
example_group_block
有了这个改变
module RepetitivelyDescribe
def repetitively_describe(*args, &example_group_block)
options = args.extract_options!
options.delete(:for).each do |item|
item_args = args.collect(&:dup) + [options.dup]
item_args[0] << " [#{item}]"
describe(*item_args) do
class_exec(item, &example_group_block)
end
end
end
end
在更改之前输出describe('foo') do
repetitively_describe "Query API", :for => %w( v1 v2 ) do |version|
it "responds with JSON"
end
end.descendant_filtered_examples.collect(&:full_description)
而不是["foo Query API [v1] responds with JSON", "foo Query API [v2] responds with JSON"]
。