我在Rails的一个功能测试中有一堆具有重复结构的代码。我想通过重复使用结构来干掉我的规格。有什么建议吗?
一个例子是:
feature "Search page"
subject { page }
it "should display results"
# do something
within "#A" do
should have_content("James")
end
within "#B" do
should have_content("October 2014")
end
# do something else
# code with similar structure
within "#A" do
should have_content("Amy")
end
within "#B" do
should have_content("May 2011")
end
end
首先,我尝试在RSpec中定义自定义匹配器,但是当我添加within
块时,它似乎不起作用。我的猜测within
是特定于Capybara的,并且不能在RSpec中的自定义匹配器中使用。
答案 0 :(得分:2)
为什么不将公共代码分解为模块中的辅助方法。然后,您可以在spec_helper.rb文件中包含该模块
我通常会将像user_login这样的常用代码放在 spec / support 文件夹
中的文件中。<强> spec_helper.rb 强>
#Load all files in spec/support
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
#some config
config.include LoginHelper
#more config
end
<强>规格/支持/ login_helper.rb 强>
module LoginHelper
def do_login(username, password)
visit root_path
within("#LoginForm") do
fill_in('username', :with => username)
fill_in('password', :with => password)
click_button('submit')
end
end
end
答案 1 :(得分:0)
我不认为您使用within
作为匹配器,因为匹配器将在should
,should_not
等之后使用。您可以加载自定义,非通过编写模块并将其包含在spec_helper.rb
配置块中,将matcher方法纳入您的规范,例如:
<强>规格/支持/ my_macros.rb 强>
module MyMacros
def within(tag, &block)
# your code here
end
end
<强>规格/ spec_helper.rb 强>
require 'spec/support/my_macros'
...
RSpec.configure do |config|
config.include MyMacros
...
end
答案 2 :(得分:0)
我使用Capybara + Cucumber进行端到端测试。最后,我认为我已经完成了@hraynaud和@eirikir所建议的(定向) - 尽管细节因为我在黄瓜背景下的不同而有所不同。所以,请考虑这不是一个完全不同的想法 - 但可能是一个稍微更完整的描述和讨论。另请注意,我的示例侧重于测试结果 - 而不是导航和表单填写。既然看起来你处于测试心态(考虑到你使用should have_content
),我认为这可能会引起人们的兴趣。
一般来说,我的方法是:
module
内的验证辅助方法中包裹Capybara测试。包装的动机是(a)使我不必记住Capybara语法和(b)避免必须键入所有那些重复的测试语句。此外,它最终使我的测试更清晰,更可读(至少对我而言)。validate
方法,该方法接收(i)验证助手方法名称(作为符号)和(ii)项目数组,每个项目都将传递给验证助手。 validate方法简单地遍历项目数组并调用验证帮助器方法(使用send
方法),并将每个项目与每次调用一起传递。步骤1-3发生在名为form_validation_helpers.rb
的文件中。
功能性/支撑性/ form_validation_helpers.rb 强>
module FormValidationHelpers
...more methods before
# ============================================================================
# Tests that an element is on the page
# ============================================================================
def is_present(element)
expect(find(element)).to be_truthy
end
# ============================================================================
# Tests for the number of times an element appears on a page
# ============================================================================
def number_of(options={})
page.should have_css(options[:element], count: options[:count])
end
# ============================================================================
# Checks that a page has content
# ============================================================================
def page_has_content(content)
page.should have_content(content)
end
...more methods after
# ============================================================================
# The generic validation method
# ============================================================================
def validate(validation, *items)
items.each do |item|
send(validation, item)
end
end
end
World(FormValidationHelpers)
步骤4(从上面)发生在我的步骤文件中。
功能/ step_definitions / sample_steps.rb 强>
Then(/^she sees the organization events content$/) do
validate :number_of,
{element: 'ul#organization-tabs li.active', is: 1}
validate :is_present,
"ul#organization-tabs li#events-tab.active"
validate :page_has_content,
"A Sample Organization that Does Something Good",
"We do all sorts of amazing things that you're sure to love."
end
从validate :page_has_content
示例中可以看出,我可以通过在validate
调用上添加适当的参数来多次运行测试(因为validate
方法在第一个之后收到所有内容参数到数组中)。
我喜欢在我的测试中使用非常具体的选择器 - 所以我可以确定我正在测试正确的元素。但是,当我开始更改我的视图文件时,我开始打破我的测试(坏),我必须返回并修复我的测试中的所有选择器 - 无论它们在哪里。所以,我制作了一堆选择器助手,并将它们连接到World,就像上面一样。
功能性/支撑性/ form_selectors_helpers.rb 强>
module FormSelectorsHelper
...more _selector methods before
def event_summary_selector
return 'input#event_summary[type="text"]'
end
...more _selector methods after
end
World(FormSelectorsHelper)
所以现在,我只有一个地方需要让我的选择器保持最新和准确。用法如下(请注意,我可以传递验证助手方法所需的任何内容 - 字符串,方法,哈希,数组等)...
功能/ step_definitions / more_sample_steps.rb 强>
Then(/^she sees new event form$/) do
validate :is_present,
event_summary_selector,
start_date_input_selector,
start_time_input_selector,
end_time_input_selector
validate :is_absent,
end_date_input_selector
validate :is_unchecked,
all_day_event_checkbox_selector,
use_recur_rule_checkbox_selector
validate :is_disabled,
submit_button_selector
validate :has_text,
{ element: modal_title_bar_selector, text: "Okay, let's create a new event!" }
end
回到你的问题,我想你最终会得到类似的东西:
feature "Search page"
subject { page }
it "should display results"
# do something
validate :has_content_within,
[a_selector, "James"],
[b_selector, "October 2014"]
# do something else
validate :has_content_within,
[a_selector, "Amy"],
[b_selector, "May 2011"]
end
答案 3 :(得分:0)
Capybara Test Helpers提供了一种在使用Capybara + RSpec时封装测试代码的好方法。
RSpec.feature "Search page", test_helpers: [:search] do
before do
visit search_path
end
it "should display results"
search.filter_by(name: 'James')
search.should.have_result(name: 'James', date: 'October 2014')
search.filter_by(name: 'Amy')
search.should.have_result(name: 'Amy', date: 'May 2011')
end
end
然后您可以根据需要实现自己的操作和断言:
class SearchTestHelper < Capybara::TestHelper
aliases(
name_container: '#A',
date_container: '#B',
)
def filter_by(attrs)
attrs.each { |key, name| ... }
click_link('Search')
end
def have_result(name:, date:)
have(:name_container, text: name)
within(:date_container) { have_content(date) } # equivalent
end
end