是的,您可以从自定义匹配器中调用内置的 rspec 匹配器。换句话说,您可以在编写匹配器时使用普通的 Rspec DSL 而不是纯 Ruby。看看这个要点(不是我的要点,但它帮助了我!)。
我有一个非常复杂的控制器,带有一个选项卡式界面,其中定义和选择的选项卡取决于模型实例的状态。我需要在 :new、:create、:edit 和 :update 操作的每个状态下测试选项卡设置。所以我写了这些匹配器:
require "rspec/expectations"
RSpec::Matchers.define :define_the_review_tabs do
match do
expect(assigns(:roles )).to be_a_kind_of(Array)
expect(assigns(:creators )).to be_a_kind_of(ActiveRecord::Relation)
expect(assigns(:works )).to be_a_kind_of(Array)
expect(assigns(:available_tabs)).to include("post-new-work")
expect(assigns(:available_tabs)).to include("post-choose-work")
end
match_when_negated do
expect(assigns(:roles )).to_not be_a_kind_of(Array)
expect(assigns(:creators )).to_not be_a_kind_of(ActiveRecord::Relation)
expect(assigns(:works )).to_not be_a_kind_of(Array)
expect(assigns(:available_tabs)).to_not include("post-new-work")
expect(assigns(:available_tabs)).to_not include("post-choose-work")
end
failure_message do
"expected to set up the review tabs, but did not"
end
failure_message_when_negated do
"expected not to set up review tabs, but they did"
end
end
RSpec::Matchers.define :define_the_standalone_tab do
match do
expect(assigns(:available_tabs)).to include("post-standalone")
end
match_when_negated do
expect(assigns(:available_tabs)).to_not include("post-standalone")
end
failure_message do
"expected to set up the standalone tab, but did not"
end
failure_message_when_negated do
"expected not to set up standalone tab, but they did"
end
end
RSpec::Matchers.define :define_only_the_review_tabs do
match do
expect(assigns).to define_the_review_tabs
expect(assigns).to_not define_the_standalone_tab
expect(assigns(:selected_tab)).to eq(@selected) if @selected
end
chain :and_select do |selected|
@selected = selected
end
failure_message do
if @selected
"expected to set up only the review tabs and select #{@selected}, but did not"
else
"expected to set up only the review tabs, but did not"
end
end
end
RSpec::Matchers.define :define_only_the_standalone_tab do
match do
expect(assigns).to define_the_standalone_tab
expect(assigns).to_not define_the_review_tabs
expect(assigns(:selected_tab)).to eq("post-standalone")
end
failure_message do
"expected to set up only the standalone tab, but did not"
end
end
RSpec::Matchers.define :define_all_tabs do
match do
expect(assigns).to define_the_review_tabs
expect(assigns).to define_the_standalone_tab
expect(assigns(:selected_tab)).to eq(@selected) if @selected
end
chain :and_select do |selected|
@selected = selected
end
failure_message do
if @selected
"expected to set up all three tabs and select #{@selected}, but did not"
else
"expected to set up all three tabs, but did not"
end
end
end
我像这样使用它们:
should define_all_tabs.and_select("post-choose-work")
should define_all_tabs.and_select("post-standalone")
should define_only_the_standalone_tab
should define_only_the_review_tabs.and_select("post-choose-work")
should define_only_the_review_tabs.and_select("post-new-work")
无需用纯 Ruby 编写匹配器,就能将多个重复的期望块合并成一组自定义匹配器,这真是太棒了。
这为我节省了数十行代码,使我的测试更具表现力,并允许我在填充这些选项卡的逻辑发生变化时在一个地方进行更改。
另请注意,您可以在自定义匹配器中访问方法/变量,例如assigns
and ,controller
因此您不需要显式传递它们。
最后,我可以在规范中内联定义这些匹配器,但我选择将它们放在 spec/support/matchers/controllers/posts_controller_matchers.rb