我希望在许多RSpec共享示例中包含一个方法来覆盖默认上下文方法:
describe 'Links' do
describe EditLink do
it_behaves_like 'update association service', :link do
def create_association(params, user_symbol)
AddLink.new(user_symbol).(params)
end
end
end
describe DeleteLink do
it_behaves_like 'delete association service', :link do
def create_association(params, user_symbol)
AddLink.new(user_symbol).(params)
end
end
end
end
有没有办法提取create_association
并在共享示例中传递它,以便它将覆盖共享上下文中的默认方法,而不创建那些do ... end
块并在每个{{1}内复制粘贴此方法}}?
答案 0 :(得分:2)
您应该可以使用RSpec helper methods执行此操作。
在某处添加带有提取方法的模块,例如。 spec/support/helpers.rb
:
module Helpers
def create_association(params, user_symbol)
AddLink.new(user_symbol).(params)
end
end
确保rails_helper.rb
中需要这样做。通常,使用默认RSpec设置时,spec/support
下的任何内容都是必需的。
RSpec需要配置为使用模块扩展共享示例组。这可以在rails_helper.rb
配置部分中完成:
RSpec.configure do |config|
config.extend Helpers
end
然后可以从共享示例块中调用该方法:
it_behaves_like 'delete association service', :link do
create_association(params, user_symbol)
end
文档解释了将模块限制为特定组的其他选项。
要在正常示例中使用帮助程序(例如,在共享示例之外),您需要include
代替extend
(或除此之外):
RSpec.configure do |config|
config.include Helpers
end
答案 1 :(得分:0)
首先,注意:如果这两个示例的create_association
覆盖相同,并且它们都在示例代码中的外部上下文中,则不必将其声明两次 - - 只需在外部语境中声明它:
describe 'Links' do
def create_association(params, user_symbol)
AddLink.new(user_symbol).(params)
end
describe EditLink do
# ... (etc.)
end
describe DeleteLink do
# ... (etc.)
end
end
第二:我还没想出如何覆盖共享示例方法(我仍然怀疑有一种方法,在方法调用站点上的一些东西会使它解析为重新定义的版本,但我太新了Ruby已经弄明白了,但我找到了以下解决方法:
RSpec.shared_examples 'an association service' do
def create_association(params, user_symbol)
if defined? create_association_override
create_association_override
else
# ...whatever the default value is...
end
end
end
在规范中:
describe 'Links' do
def create_association_override(params, user_symbol)
AddLink.new(user_symbol).(params)
end
# ...(etc.)
伪造虚拟方法查找是一种丑陋的方式,如果我从来没有听说过更好的方法,我会非常惊讶,但它确实有效。
答案 2 :(得分:0)
我不确定你想要完成的是一般的好方法。它闻起来像你需要重新思考和重构你的RSpec例子。您将需要进行一些错误检查以确保 您用于创建关联的所有方法都是有效的,并且您传入的任何参数同样没有错误。这引入了脆弱性。此外,您可能会牺牲可读性,从而增加维护成本。
尽管如此,我创建了示例代码来说明如何完成此操作。这并不复杂。
基本上,你想完成两件事:
association
将shared_example的参数设为可选参数
如果没有将任何参数传递给shared_example,它应该只使用默认方法:
it_behaves_like 'update association service'
但是如果你做传入参数,那么使用这些参数(方法和参数等)来创建关联:
it_behaves_like 'update association service', DeleteLink, :new, []
上面的参数是要实例化的类,用于创建关联的方法(发送给类),以及的参数方法。(论证显然取决于你如何编写共享示例。)
这只是Ruby。在您的shared_example中,只需检查参数是否为nil,然后相应地执行操作。这是基本的想法:
RSpec.shared_examples 'update association service' do |optional_class, optional_method, parameters|
let(:assoc) {
if optional_class.nil? || optional_method.nil?
default_assoc # defined prior to this (needs error checking)
# See the gist for a full example
else
optional_class.send(optional_method, *parameters)
end
}
it 'does something with the assoc' do
expect(assoc.behavior_tested).to eq expected_behavior
end
end
`
以下是您在Rspec示例组中使用它的方法:
RSpec.describe 'Links' do
# set the default method. In the gist, I use a shared_context to do this
let(:default_assoc) {
default_class.send(default_method, arg1, arg2)
}
describe 'AddLink' do
# Since no arguments are passed to this shared example,
# it will use 'default_assoc' in the shared_example.
it_behaves_like 'update association service'
end
describe 'EditLink' do
# Override how the association is created in the shared_example
# by passing in arguments that are used to create it:
it_behaves_like 'update association service', MySpecialAssocClass, :special_create_method, [arg1, arg2, arg3, arg4]
end
end
我在Github上的一个充分的工作示例:weedySeaDragon - rspec-shared-context-examples-optional.rb