浩文 联系 关于本站 登录 注册

README

commit信息:Prepare for 3.33.0 release | 提交者:twalpole | 提交时间:2020-06-22 | 版本:3c675c2

Capybara

Build Status
Build Status
Code Climate
Coverage Status
Gitter
SemVer

注意你正在查看Capybara开发版的README文件。如果你正在使用当前发行版本,该README文件是在这里 https://github.com/teamcapybara/capybara/blob/3.33_stable/README.md

Capybara通过模拟真实用户与你应用的交互来帮你测试web应用。它与运行测试的driver无关,并且内置了Rack::Test和Selenium支持。它可以使用一个扩展gem来支持WebKit。

支持Capybara

如果你或者你的公司发现了 Capybara的价值,并且想要给它提供财务贡献来支持它的持续维护和开发,请查看
Patreon

需要帮助?请在邮件列表发问(请不要在GitHub上开启一个issue):http://groups.google.com/group/ruby-capybara

目录

主要优势

  • 不用特别设置 Rails 和 Rack应用。开箱即用。
  • 直观的API 它模拟真实用户使用的语言。
  • 选择测试的后台 无需修改测试,会从快速的无头模式转为真实浏览器模式。
  • 强大的同步 功能意味着你不再需要手动等待异步程序完成。

建立

Capybara 依赖Ruby 2.5.0以上版本。把下面这行添加到你的Gemfile文件中,然后运行 bundle install

gem 'capybara'

如果你正在测试Rails应用,把下面这行代码添加到你的测试帮助方法文件中:

require 'capybara/rails'

如果你正在测试Rack应用,但不是Rails,请给Capybara.app设置你的Rack应用:

Capybara.app = MyRackApp

如果你需要测试JavaScript,或者你的应用需要跟一个远程URL(或者位于远程的)交互,你需要使用不同的driver。如果你正在使用Rails5以上的版本,但是不是从Rails5.1以上版本使用系统测试,为了匹配Rails 的默认设置,你将需要改变要使用的"server"为puma来启动你的应用。

Capybara.server = :puma # Until your setup is working
Capybara.server = :puma, { Silent: true } # 用来清理测试输出。

搭配Cucumber使用Capybara

Capybara内建支持cucumber-rails gem。如果你不使用Rails,请手动加载capybara/cucumber模块:

require 'capybara/cucumber'
Capybara.app = MyRackApp

你可以像下面这样,在你的步骤中使用Capybara DSL:

When /I sign in/ do
  within("#session") do
    fill_in 'Email', with: 'user@example.com'
    fill_in 'Password', with: 'password'
  end
  click_button 'Sign in'
end

你可以通过搭配@javascript来标记场景(或者功能),以便切换到 Capybara.javascript_driver(默认是:selenium):

@javascript
Scenario: do something Ajaxy
  When I click the Ajax link
  ...

还有一些提供给你的每个注册driver的明确标签(@selenium, @rack_test, 等)。

搭配RSpec使用Capybara

添加下面这行来加载RSpec 3.5+支持,(通常添加到spec_helper.rb文件中):

require 'capybara/rspec'

如果你正在使用Rails,把你的Capybara specs放到spec/features或者spec/system里(仅在RSpec中进行了配置 后才会生效),并且在不同的文件夹里有 Capybara specs, 请使用type: :feature或者type: :system(依据你正在写的测试类型)来标记你的example组。

如果你不使用Rails,请使用type: :feature来标记你想使用Capybara的example组。

现在,你可以像下面这样写你的specs:

describe "the signin process", type: :feature do
  before :each do
    User.make(email: 'user@example.com', password: 'password')
  end

  it "signs me in" do
    visit '/sessions/new'
    within("#session") do
      fill_in 'Email', with: 'user@example.com'
      fill_in 'Password', with: 'password'
    end
    click_button 'Sign in'
    expect(page).to have_content 'Success'
  end
end

使用js: true来选择使用 Capybara.javascript_driver(默认是:selenium),或者使用:driver选项来指定特定的driver。例如:

describe 'some stuff which requires js', js: true do
  it 'will use the default js driver'
  it 'will switch to one specific driver', driver: :apparition
end

Capybara 内建了DSL来创建可描述性的验收测试:

feature "Signing in" do
  background do
    User.make(email: 'user@example.com', password: 'caplin')
  end

  scenario "Signing in with correct credentials" do
    visit '/sessions/new'
    within("#session") do
      fill_in 'Email', with: 'user@example.com'
      fill_in 'Password', with: 'caplin'
    end
    click_button 'Sign in'
    expect(page).to have_content 'Success'
  end

  given(:other_user) { User.make(email: 'other@example.com', password: 'rous') }

  scenario "Signing in as another user" do
    visit '/sessions/new'
    within("#session") do
      fill_in 'Email', with: other_user.email
      fill_in 'Password', with: other_user.password
    end
    click_button 'Sign in'
    expect(page).to have_content 'Invalid email or password'
  end
end

feature实际上是describe ..., type: :feature的一个别名,
backgroundbefore的别名, scenarioit的别名,以及given/given!let/let!的别名。

视图specs也支持Capybara匹配:

RSpec.describe "todos/show.html.erb", type: :view do
  it "displays the todo title" do
    assign :todo, Todo.new(title: "Buy milk")

    render

    expect(rendered).to have_css("header h1", text: "Buy milk")
  end
end

请注意:当你需要安装'capybara/rspec'代理方法来绕过Capybara::DSL方法 all/within和RSpec内建的同名匹配器间的命名冲突时。如果你选择不依赖 'capybara/rspec',你可以通过在请求 RSpec 和 'capybara/dsl'之后再请求'capybara/rspec/matcher_proxies'来安装该代理方法

搭配Test::Unit使用Capybara

  • 如果你正在使用Test::Unit,请像下面这样给Capybara定义一个基类:

    require 'capybara/dsl'
    
    class CapybaraTestCase < Test::Unit::TestCase
      include Capybara::DSL
    
      def teardown
        Capybara.reset_sessions!
        Capybara.use_default_driver
      end
    end
    

搭配Minitest使用Capybara

  • 如果你正在使用Rails,但未使用Rails系统测试,请把下面代码添加到你的 test_helper.rb文件里来保证 Capybara在所有派生自ActionDispatch::IntegrationTest的测试中起效:

    require 'capybara/rails'
    require 'capybara/minitest'
    
    class ActionDispatch::IntegrationTest
      # Make the Capybara DSL available in all integration tests
      include Capybara::DSL
      # Make `assert_*` methods behave like Minitest assertions
      include Capybara::Minitest::Assertions
    
      # Reset sessions and driver between tests
      teardown do
        Capybara.reset_sessions!
        Capybara.use_default_driver
      end
    end
    
  • 如果你不使用rails,请像下面这样给你的Capybara测试定义一个基类:

    require 'capybara/minitest'
    
    class CapybaraTestCase < Minitest::Test
      include Capybara::DSL
      include Capybara::Minitest::Assertions
    
      def teardown
        Capybara.reset_sessions!
        Capybara.use_default_driver
      end
    end
    

    请记着在那些复写了 teardown方法的子类里调用super方法。

要切换driver,请设置Capybara.current_driver。例如:

class BlogTest < ActionDispatch::IntegrationTest
  setup do
    Capybara.current_driver = Capybara.javascript_driver # :selenium by default
  end

  test 'shows blog posts' do
    # ... this test is run with Selenium ...
  end
end

搭配Minitest::Spec使用Capybara

遵从以上Minitest结构,并且添加capybara/minitest/spec依赖

page.must_have_content('Important!')

Drivers

Capybara使用相同的DSL来驱动各种浏览器和无头drivers。

选择Driver

默认情况下,Capybara使用 :rack_test driver, 它很快但有限制:它不支持JavaScript,它不能获取你的Rack应用之外的 HTTP资源,像是远程API和OAuth服务。为了绕过这些限制,你可以给你的功能设置不同的driver,例如,如果希望在Selenium里运行所有东西,你可以像下面这样做:

Capybara.default_driver = :selenium # :selenium_chrome and :selenium_chrome_headless are also registered

然而如果你的应用正在使用RSpec或者Cucumber(并且你的应用离开了JS能正确运行),你或许想使用更快的 :rack_test来设置default_driver值,同时通过使用js: true或者@javascript来让剩下的测试使用JavaScript-capable driver。默认情况下,JavaScript测试是使用:selenium driver来运行,你可以通过设置Capybara.javascript_driver选项来改变该默认行为。

你还可以临时改变driver(通常在Before/setup和After/teardown块儿里):

Capybara.current_driver = :apparition # 临时选择不同的driver
# tests here
Capybara.use_default_driver       # 改为默认的driver

请注意:改变driver会创建新的Session,因此你不能在一个测试的中途改变driver。

RackTest

RackTest 是Capybara的默认driver。它是纯Ruby写的,并且不提供执行JavaScript的支持。因为RackTest driver直接与Rack 接口交互,它不需要运行server。因此那意味着,如果你的应用不是一个Rack应用(Rails,
Sinatra或者其它的Rack应用的Ruby框架),你将不能使用该driver。另外,你不能使用该driver来测试一个远程应用,或者访问远程URLs(像是跳转外部站点,外部API或OAuth服务)。

capybara-mechanize提供了一个类似的driver,它可以访问远程服务。

RackTest 可以像下面这样使用一组headers进行配置:

Capybara.register_driver :rack_test do |app|
  Capybara::RackTest::Driver.new(app, headers: { 'HTTP_USER_AGENT' => 'Capybara' })
end

请查阅关于添加和配置drivers的章节。

Selenium

Capybara支持Selenium 3.5+
(Webdriver)
。为了使用Selenium,你需要安装selenium-webdriver gem,并且如果你正在使用bundler,请把它添加到你的Gemfile文件中。

Capybara已经注册了一些使用Selenium的命名drivers,-- 它们是:

  • :selenium => Selenium driving Firefox
  • :selenium_headless => Selenium driving Firefox in a headless configuration
  • :selenium_chrome => Selenium driving Chrome
  • :selenium_chrome_headless => Selenium driving Chrome in a headless configuration

这些应该在桌面环境配置中(搭配要安装的相关软件)起效,但是,如果你使用CI环境则需要把附加选项传递给浏览器,你需要定制它们。请查看关于添加和配置drivers的章节。

请注意:把server运行在不同线程上的drivers,或许不能分享你测试中的同一个事务,从而造成不能在你的测试和测试server间分享数据,请查看事务和数据库配置

Apparition

apparition driver是一个新的driver,它让你可以把你的测试运行在chrome的有头或者无头配置中。它尝试提供Poltergeist driver API
capybara-webkit API的向后兼容,并且让你可以使用现代JS/CSS。它使用CDP来与chrome交流,这样,可以不使用chromedriver。 该driver是被Capybara的当前开发者给开发,并且尝试配合新版Capybara来保持同步更新。当它达到v1.0时,它可能会被移到teamcapybara repo 。

The DSL

请注意:默认Capybara只查找可视化元素,这是因为一个真实的用户不会与不可见元素交互。

请注意:在Capybara中的所有检索是区分大小写。那是因为Capybara主要使用XPath,它不支持大小写不敏感。

导航

你可以使用visit方法来导航到其它页面:

visit('/projects')
visit(post_comments_path(post))

该visit方法只接受一个参数,它的请求方式 始终为 GET

你可以得到浏览会话的current path,并使用have_current_path matcher匹配者来测试它:

expect(page).to have_current_path(post_comments_path(post))

请注意:你也可以直接使用current_path的值来断言当前路径。然而,使用have_current_path匹配更安全,因为使用Capybara的waiting behaviour可以保证动作已经被完成(像是click_link)。

点击链接和按钮

完整参考:Capybara::Node::Actions

你可以通过跟踪链接和buttons来与你的web应用交互。Capybara会追踪所有的转向,以及与按钮相关的提交表单。

click_link('id-of-link')
click_link('Link Text')
click_button('Save')
click_on('Link Text') # clicks on either links or buttons
click_on('Button Value')

表单交互

完整参考:Capybara::Node::Actions

下面有许多工具用来与form元素交互:

fill_in('First Name', with: 'John')
fill_in('Password', with: 'Seekrit')
fill_in('Description', with: 'Really Long Text...')
choose('A Radio Button')
check('A Checkbox')
uncheck('A Checkbox')
attach_file('Image', '/path/to/image.jpg')
select('Option', from: 'Select Box')

查询

完整参考:Capybara::Node::Matchers

Capybara有丰富的设置选项来查询某些元素的存在,然后使用和操控这些元素。

page.has_selector?('table tr')
page.has_selector?(:xpath, './/table/tr')

page.has_xpath?('.//table/tr')
page.has_css?('table tr.foo')
page.has_content?('foo')

请注意: has_no_selector?导航与not
has_selector?
不同。寻找一个解释,请查看异步JavaScript章节。

你可以搭配RSpec的魔术匹配来使用这些功能:

expect(page).to have_selector('table tr')
expect(page).to have_selector(:xpath, './/table/tr')

expect(page).to have_xpath('.//table/tr')
expect(page).to have_css('table tr.foo')
expect(page).to have_content('foo')

查找

完整参考:Capybara::Node::Finders

你也可以查找一些特定的元素,以便操作它们:

find_field('First Name').value
find_field(id: 'my_field').value
find_link('Hello', :visible => :all).visible?
find_link(class: ['some_class', 'some_other_class'], :visible => :all).visible?

find_button('Send').click
find_button(value: '1234').click

find(:xpath, ".//table/tr").click
find("#overlay").find("h1").click
all('a').each { |a| a[:href] }

如果你需要通过一些参数/属性来查找一些元素,你也可以传递一个过滤器块儿,它们将在正常的waiting behavior中被检查。如果你发现你经常需要这样做,你最好添加一个定制的选择器,或者向现有的选择器中添加一个过滤器

find_field('First Name'){ |el| el['data-xyz'] == '123' }
find("#img_loading"){ |img| img['complete'] == true }

请注意find将等待元素在页面出现,在Ajax章节有解释。如果元素没有出现,它将会触发一个错误。

这些元素都有Capybara DSL的所有方法,因此你可以将它们限制为页面的特定部分:

find('#navigation').click_link('Home')
expect(find('#navigation')).to have_button('Sign out')

Scoping

Capybara让你可以将某些操作限制到页面的指定区域(例如与表单进行交互或单击链接和按钮)。你可以使用普通的within方法来达到该目的。(可选)你可以指定使用哪个选择器。

within("li#employee") do
  fill_in 'Name', with: 'Jimmy'
end

within(:xpath, ".//li[@id='employee']") do
  fill_in 'Name', with: 'Jimmy'
end

有专业的方法将范围限制为由ID或字段集的图例标签的文本标识的特定字段集,对于表格,它是由id或者表格的caption标签所标识。

within_fieldset('Employee') do
  fill_in 'Name', with: 'Jimmy'
end

within_table('Employee') do
  fill_in 'Name', with: 'Jimmy'
end

使用窗口

Capybara提供了一些简化查找和选择窗口的方法:

facebook_window = window_opened_by do
  click_button 'Like'
end
within_window facebook_window do
  find('#login_email').set('a@example.com')
  find('#login_password').set('qwerty')
  click_button 'Submit'
end

编写脚本

在支持它的drivers中,你可以轻松地执行JavaScript:

page.execute_script("$('body').empty()")

对于简单的表达式,你可以返回脚本的结果。

result = page.evaluate_script('4 + 4');

对于更复杂的脚本,你需要把它们写为一个表达式。

result = page.evaluate_script(<<~JS, 3, element)
  (function(n, el){
    var val = parseInt(el.value, 10);
    return n+val;
  })(arguments[0], arguments[1])
JS

Modals

在支持它的drivers中,你可以接受、关闭和回复alerts对话框、confirms对话框和提示对话框。

你可以通过包装代码,即在一个块儿里创建alert,来接受或者关闭alert信息。

accept_alert do
  click_link('Show Alert')
end

你可以通过把confirmation包装到块里,来接受或者取消一个confirmation提示框,就像下面这样:

dismiss_confirm do
  click_link('Show Confirm')
end

你也可以接受或者关闭提示对话框,并且可以提供一个文本来填写回复:

accept_prompt(with: 'Linus Torvalds') do
  click_link('Show Prompt About Linux')
end

modal的方法会返回出示信息,因此你可以通过给一个变量设置该返回值到来获取该提示信息:

message = accept_prompt(with: 'Linus Torvalds') do
  click_link('Show Prompt About Linux')
end
expect(message).to eq('Who is the chief architect of Linux?')

调试

拍摄当前页面的快照并查看一下可能会很有用:

save_and_open_page

你也可以使用page.html来获取DOM的当前状态字符串。

print page.html

这对于调试非常有用。你应避免测试page.html的内容,并且使用更具表现力的finder方法来代替。

最后,在支持它的drivers中,你可以保存屏幕截图:

page.save_screenshot('screenshot.png')

或者保存并自动打开:

save_and_open_screenshot

截屏被保存到相对于应用目录的Capybara.save_path。如果你请求了capybara/railsCapybara.save_path地址将默认是tmp/capybara

匹配

可以定制Capybara如何发现元素。你有有两种选择,Capybara.exactCapybara.match

精确

Capybara.exact和该exact选项搭配着XPath gem中的is表达式一起工作。当exact是true时,所有的is表达是会精确匹配,当它被设置为false时,它们也可以匹配子字符串。许多内建于Capybara中的选择器,使用该is表达式。这种方式让你指定是否可以匹配子字符串。Capybara.exact被默认设置为false。

例如:

click_link("Password") # 也会匹配 "Password confirmation"
Capybara.exact = true
click_link("Password") # 不会匹配 "Password confirmation"
click_link("Password", exact: false) # 可以被复写

策略

使用Capybara.match和其等价的match选项,你可以控制在一个查询匹配到多个元素时Capybara的行为。当前Capybara内建了4种不同的策略:

  1. first: 只选择匹配的第1个元素。
  2. one: 如果匹配的元素多于一个,将会引发一个错误。
  3. smart: 如果exact选项被设置为true,在匹配的元素多于一个时,会引发一个错误,就像one一样。如果exact是false,它将首先试着精确匹配,如果发现的元素多于一个,它将引发一个错误。如果没有发现任何元素,会创建一个新的搜索,该搜索会允许部分匹配,如果该搜索返回多个匹配,将会引发一个错误。
  4. prefer_exact:如果匹配到多个元素,这些元素中的一些是精确的匹配,其它的一些是不精确的匹配,然后第1个精确匹配的元素将会被返回。

Capybara.match默认是:smart,为了模拟Capybara 2.0.x中的行为,请设置Capybara.match:one,为了模拟Capybara 1.x 的行为,请设置Capybara.match:prefer_exact

事务和数据库配置

请注意: Rails 5.1以上能在应用线程和测试线程间安全的共享数据库链接。因此,如果你正在使用rails5.1以上的版本,你可以忽略该章节。

一些Capybara drivers需要运行一个真实的HTTP服务。Capybara了解该需求,并且在测试进程里开启一个HTTP服务,但是,该服务是在另一个线程里。Selenium是这些drivers的其中之一,然而,RackTest不是。

如果你正在使用SQL数据库,每个测试通常运行在一个事物里,那意味着在每个测试结束时都会roll back数据,例如,rspec-rails默认这样做。由于事务通常不能在线程间共享。这会造成在你的测试代码里提交到数据库中的数据,对于Capybara来说是不可见的。

Cucumber使用truncation代替事务来支持该功能,即它们在每次测试后都会清空整个数据库。你也可以使用一个像database_cleaner这样的工具来取得相同的功能。

异步JavaScript(Ajax and friends)

当与异步JavaScript一起工作时,你或许会遇到一些情景,那时你试图与还不存在于页面的元素交互。Capybara会自动处理这种情况,它会等待这些还未出现在页面中的元素。

当发出下面这些DSL结构时:

click_link('foo')
click_link('bar')
expect(page).to have_content('baz')

如果点击 foo 链接触发了一个异步程序,像是一个Ajax请求,当完成了该请求后会把bar链接添加到页面,点击该bar链接预期会失败,因为该链接至今还不存在。Capybara很聪明会在放弃并且抛出错误之前的短暂时间内重试查找该链接。下一行的代码也一样,它会寻找页面中的baz内容;它将在短暂时间内重新查找该内容。你可以调整该等待时间是多长(默认是两秒钟):

Capybara.default_max_wait_time = 5

请注意,由于这种行为,下面的两行代码是等价的,你应该始终使用下面这行!

!page.has_xpath?('a')
page.has_no_xpath?('a')

第1行的代码将马上失败,因为该内容还没有被移除。但是下面这行将等待异步程序把该内容从页面中移除。

但是Capybara的RSpec匹配是足够聪明,它支持每一种表达。
下面的两行代码是功能等价的:

expect(page).not_to have_xpath('a')
expect(page).to have_no_xpath('a')

Capybara的等待行为很高级,它能处理下面这行代码的情况:

expect(find('#sidebar').find('h1')).to have_content('Something')

甚至如果JavaScript造成页面显示#sidebar元素,Capybara将会重新加载它和它包含的所有元素。如果一个AJAX请求造成#sidebar的内容改变了,它更新h1的文本为"Something",当发生这种事时,该测试将会通过。如果你不想要这种行为,你可以设置Capybara.automatic_reloadfalse

在其它地方使用DSL

你可以通过包含Capybara::DSL来把DSL混入到任意内容中:

require 'capybara/dsl'

Capybara.default_driver = :webkit

module MyModule
  include Capybara::DSL

  def login!
    within(:xpath, ".//form[@id='session']") do
      fill_in 'Email', with: 'user@example.com'
      fill_in 'Password', with: 'password'
    end
    click_button 'Sign in'
  end
end

这使其可以在不受支持的测试框架中使用,也可以用于通用脚本。

调用远程服务器

通常Capybara用来测试内部的Rack应用,但是你也可以通过设置app_host来让它与因特网中任意的web服务器通信:

Capybara.current_driver = :selenium
Capybara.app_host = 'http://www.google.com'
...
visit('/')

请注意: 默认的driver (:rack_test) 不支持远程服务器。使用支持该功能的drivers,你可以直接访问URL:

visit('http://www.google.com')

默认Capybara会试着自动启动一个rack应用。如果你正对着一个远程应用运行测试,你可能想要关闭Capybara的rack应用:

Capybara.run_server = false

使用sessions

Capybara管理命名的sessions(如果未指定,则为:default)来让多个sessions可以用同一个driver与同一个测试应用实例交互。如果找不到使用当前driver和测试应用程序实例的给定名称的会话,则将使用当前驱动程序创建一个新会话。

命名的sessions

为了在不同的session里操作,然后再返回之前的session

Capybara.using_session("Bob's session") do
   #在Bob浏览器session中做一些事情
end
 #返回之前的session

将当前session永久切换到其它session

Capybara.session_name = "some other session"

手动使用sessions

为了一些高级操控,你可以手动实例化和使用Session

require 'capybara'

session = Capybara::Session.new(:webkit, my_rack_app)
session.within("form#session") do
  session.fill_in 'Email', with: 'user@example.com'
  session.fill_in 'Password', with: 'password'
end
session.click_button 'Sign in'

XPath, CSS and selectors

Capybara不试着猜测你给它哪种选择器,它将默认总是使用CSS。如果你想使用XPath,你需要这样做:

within(:xpath, './/ul/li') { ... }
find(:xpath, './/ul/li').text
find(:xpath, './/li[contains(.//a[@href = "#"]/text(), "foo")]').value

此外,你可以设置默认选择器为XPath:

Capybara.default_selector = :xpath
find('.//ul/li').text

Capybara提供了很多其它内建的选择器类型,在内建选择器这里可以查看完整的列表以及适用的过滤器

Capybara让你可以添加定制选择器,如果你发现你经常使用同一个类型的选择器,那将会非常有用。下面是一个非常简单的例子,它还有许多功能没有被示范。了解更多深入的例子,请查看Capybaras的内建选择器定义。

Capybara.add_selector(:my_attribute) do
  xpath { |id| XPath.descendant[XPath.attr(:my_attribute) == id.to_s] }
end

Capybara.add_selector(:row) do
  xpath { |num| ".//tbody/tr[#{num}]" }
end

Capybara.add_selector(:flash_type) do
  css { |type| "#flash.#{type}" }
end

给xpath的块必须总是返回一个String类型的Xpath表达式,或者使用Xpath gem生成的XPath表达式。你现在可以像这样使用这些选择器:

find(:my_attribute, 'post_123') # find element with matching attribute
find(:row, 3) # find 3rd row in table body
find(:flash_type, :notice) # find element with id of 'flash' and class of 'notice'

小心XPath的 // 圈套

XPath中的 // 表达式意味着一些特别的东西,它或许不是你想的那样。与大多数认知相反, // 意味着 "在文档中的任意位置",而不是在 "当前内容的任意位置"。例如:

page.find(:xpath, '//body').all(:xpath, '//script')

您可能希望它可以找到body中的所有脚本标签,但实际上,它不仅可以找到body中的所有脚本标签,还可以找到整个文档中的所有脚本标签!你想使用的是.//表达式,它表示“当前节点的任何后代”:

page.find(:xpath, '//body').all(:xpath, './/script')

within也这样:

within(:xpath, '//body') do
  page.find(:xpath, './/script')
  within(:xpath, './/table/tbody') do
  ...
  end
end

配置和添加drivers

Capybara使你可以在不同的驱动程序之间进行切换。它暴露了一个API,使用任何你需要的设置来调整这些drivers,或者添加你自己的drivers。下面是如何复写selenium driver配置来使用chrome:

Capybara.register_driver :selenium do |app|
  Capybara::Selenium::Driver.new(app, :browser => :chrome)
end

并且,它也可以给此配置起一个不同的名字。

# Note: Capybara registers this by default
Capybara.register_driver :selenium_chrome do |app|
  Capybara::Selenium::Driver.new(app, :browser => :chrome)
end

然后测试可以轻松地选择不同的浏览器:
ruby
Capybara.current_driver = :selenium_chrome

无论从块儿中返回什么都应该遵从Capybara::Driver::Base的API描述,但是它不必继承此类。许多Gems使用该API向Capybara中添加它们自己的drivers。

Selenium wiki有一些附加信息关于如何配置底层driver。

陷阱:

  • 不能从测试中访问session和请求,限制获取回复。一些drivers可以获取回复头和HTTP状态代码,但是一些drivers不提供该功能,例如Selenium。

  • 不能获取Rails的特殊标记(像是控制器),因为我们不使用rails的集成测试。

  • 冻结时间: 对于那些依赖当前日期来工作的功能,通常的做法是模拟时间。对于不支持访问单一处理clock的 ruby/platform 组合,这可能会出现问题,因为Capybara的Ajax时间使用系统时间。导致Capybara中的结果绝不会超时,并且当错误发生时只是被挂起。你仍然可以使用允许你及时穿梭而不是冻结时间的gems。Timecop是这样的gem。

  • 使用Rack::Test时,请注意是否试图查看一些绝对URLs。例如,在访问posts_pathposts_url之间不能共享一个session。如果在Action Mailer 电子邮件中要测试一个绝对URL,请设置default_url_options选项来匹配Rails的默认URL www.example.com

  • 服务错误只会在初始化服务线程中的会话里抛出。如果你正在测试某些服务错误,并且使用多个会话,请确保使用初始的会话来测试这些错误(通常是 :default)。

  • 如果打开了WebMock,你可能会遇到"Too many open files"
    错误。一个简单的page.find调用可能会导致数千个HTTP请求,直到发生超时。默认情况下,WebMock将会给这些请求中的每一个请求创建一个新的链接。为了绕过该问题,你需要开启WebMock的net_http_connect_on_start: true参数

'线程安全'模式

通常大部分的Capybara配置项是全局设置,如果你使用多个sessions,并且只想改变一个session的设置时,它会造成一些问题。为了支持这种类型的用法,Capybara现在提供了一个"线程安全"模式,可以使用下面设置来开启:

Capybara.threadsafe = true

该设置只能在任何sessions被创建前更改设置。在"线程安全"模式中,会改变下面的Capybara行为

  • 现在可以在一个session上设置大多数选项。它们可以在session创建时或者创建后被设置,并且在会话创建时默认为全局选项。一些选项不是特定session的,它们是app, reuse_server, default_driver, javascript_driver, 和 (以上的) threadsafe。通过register_driverregister_server注册的所有的drivers和servers也是全局的。
  my_session = Capybara::Session.new(:driver, some_app) do |config|
    config.automatic_label_click = true # only set for my_session
  end
  my_session.config.default_max_wait_time = 10 # only set for my_session
  Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session
  • current_driversession_name是特定线程的。这意味着using_sessionusing_driver只在当前线程中生效。

开发者

为了设置一个开发环境,只需要这样做:

bundle install
bundle exec rake  # run the test suite with Firefox - requires `geckodriver` to be installed
bundle exec rake spec_chrome # run the test suite with Chrome - require `chromedriver` to be installed

请查看CONTRIBUTING.md来了解如何发起issues以及发起拉取请求。