如何重用Capybara中的代码

时间:2014-10-14 03:38:54

标签: ruby-on-rails rspec capybara

我在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中的自定义匹配器中使用。

4 个答案:

答案 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作为匹配器,因为匹配器将在shouldshould_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),我认为这可能会引起人们的兴趣。

一般来说,我的方法是:

  1. module内的验证辅助方法中包裹Capybara测试。包装的动机是(a)使我不必记住Capybara语法和(b)避免必须键入所有那些重复的测试语句。此外,它最终使我的测试更清晰,更可读(至少对我而言)。
  2. 创建一个通用validate方法,该方法接收(i)验证助手方法名称(作为符号)和(ii)项目数组,每个项目都将传递给验证助手。 validate方法简单地遍历项目数组并调用验证帮助器方法(使用send方法),并将每个项目与每次调用一起传递。
  3. 将帮助程序和通用验证方法附加到World(阅读有关World here的更多信息),以便在我的Cucumber测试中可以使用它们。
  4. 享受快乐测试!
  5. 步骤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

您可以read the guide here