Capybara 通过模拟真实用户如何与您的应用程序交互来帮助您测试 Web 应用程序。它与运行测试的驱动程序无关,并且内置 Rack::Test 和 Selenium 支持。WebKit 通过外部 gem 支持。
如果您和/或您的公司发现 Capybara 的价值并愿意为其持续维护和开发提供资金,请访问 Patreon
需要帮助吗?询问讨论(请不要打开问题):https://github.com/orgs/teamcapybara/discussions/categories/qa
Capybara 需要 Ruby 3.0.0 或更高版本。要安装,请将此行添加到您的Gemfile
并运行bundle install
:
gem 'capybara'
如果您正在测试的应用程序是 Rails 应用程序,请将此行添加到您的测试帮助程序文件中:
require 'capybara/rails'
如果您正在测试的应用程序是 Rack 应用程序,而不是 Rails,请将 Capybara.app 设置为您的 Rack 应用程序:
Capybara . app = MyRackApp
如果您需要测试 JavaScript,或者您的应用程序与远程 URL 交互(或位于远程 URL),则需要使用不同的驱动程序。如果使用 Rails 5.0+,但不使用 5.1 中的 Rails 系统测试,您可能还需要将用于启动应用程序的“服务器”交换到 Puma,以匹配 Rails 默认值。
Capybara . server = :puma # Until your setup is working
Capybara . server = :puma , { Silent : true } # To clean up your test output
cucumber-rails
gem 内置了 Capybara 支持。如果您没有使用 Rails,请手动加载capybara/cucumber
模块:
require 'capybara/cucumber'
Capybara . app = MyRackApp
您可以在步骤中使用 Capybara DSL,如下所示:
When /I sign in/ do
within ( "#session" ) do
fill_in 'Email' , with : '[email protected]'
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
...
还有为您设置的每个注册驱动程序的显式标签( @selenium
、 @rack_test
等)。
通过添加以下行(通常添加到您的spec_helper.rb
文件中)来加载 RSpec 3.5+ 支持:
require 'capybara/rspec'
如果您使用的是 Rails,请将您的 Capybara 规格放在spec/features
或spec/system
中(仅当您在 RSpec 中配置它时才有效),并且如果您将 Capybara 规格放在不同的目录中,则使用type: :feature
或type: :system
取决于您正在编写的测试类型。
如果您使用 Rails 系统规范,请参阅其文档以选择您想要使用的驱动程序。
如果您不使用 Rails,请使用type: :feature
。
您现在可以像这样编写规格:
describe "the signin process" , type : :feature do
before :each do
User . create ( email : '[email protected]' , password : 'password' )
end
it "signs me in" do
visit '/sessions/new'
within ( "#session" ) do
fill_in 'Email' , with : '[email protected]'
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
选项来切换到一个特定驱动程序。例如:
describe 'some stuff which requires js' , js : true do
it 'will use the default js driver'
it 'will switch to one specific driver' , driver : :selenium
end
Capybara 还附带内置 DSL,用于创建描述性验收测试:
feature "Signing in" do
background do
User . create ( email : '[email protected]' , password : 'caplin' )
end
scenario "Signing in with correct credentials" do
visit '/sessions/new'
within ( "#session" ) do
fill_in 'Email' , with : '[email protected]'
fill_in 'Password' , with : 'caplin'
end
click_button 'Sign in'
expect ( page ) . to have_content 'Success'
end
given ( :other_user ) { User . create ( email : '[email protected]' , 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
, background
是before
的别名, scenario
的it
,以及given
/ given!
let
/ let!
, 分别。
最后,视图规范也支持 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 测试定义一个基类,如下所示:
require 'capybara/dsl'
class CapybaraTestCase < Test :: Unit :: TestCase
include Capybara :: DSL
def teardown
Capybara . reset_sessions!
Capybara . use_default_driver
end
end
如果您正在使用 Rails 系统测试,请参阅其文档以获取有关选择您想要使用的驱动程序的信息。
如果您使用 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
。
要切换驱动程序,请设置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 说明进行操作,另外还需要 capybara/minitest/spec
page . must_have_content ( 'Important!' )
Capybara 使用相同的 DSL 来驱动各种浏览器和无头驱动程序。
默认情况下,Capybara 使用:rack_test
驱动程序,该驱动程序速度快但有局限性:它不支持 JavaScript,也无法访问 Rack 应用程序外部的 HTTP 资源,例如远程 API 和 OAuth 服务。要解决这些限制,您可以为您的功能设置不同的默认驱动程序。例如,如果您希望在 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
,分别。默认情况下,JavaScript 测试使用:selenium
驱动程序运行。您可以通过设置Capybara.javascript_driver
来更改此设置。
您还可以临时更改驱动程序(通常在之前/设置和之后/拆卸块中):
Capybara . current_driver = :selenium # temporarily select different driver
# tests here
Capybara . use_default_driver # switch back to default driver
注意:切换驱动程序会创建一个新会话,因此您可能无法在测试中进行切换。
RackTest 是 Capybara 的默认驱动程序。它是用纯 Ruby 编写的,不支持执行 JavaScript。由于 RackTest 驱动程序直接与 Rack 接口交互,因此不需要启动服务器。但是,这意味着如果您的应用程序不是 Rack 应用程序(Rails、Sinatra 和大多数其他 Ruby 框架都是 Rack 应用程序),那么您无法使用此驱动程序。此外,您不能使用 RackTest 驱动程序来测试远程应用程序,或访问您的应用程序可能与之交互的远程 URL(例如,重定向到外部站点、外部 API 或 OAuth 服务)。
capybara-mechanize 提供了一个类似的驱动程序,可以访问远程服务器。
RackTest 可以配置一组标头,如下所示:
Capybara . register_driver :rack_test do | app |
Capybara :: RackTest :: Driver . new ( app , headers : { 'HTTP_USER_AGENT' => 'Capybara' } )
end
请参阅有关添加和配置驱动程序的部分。
Capybara 支持 Selenium 3.5+ (Webdriver)。为了使用 Selenium,您需要安装selenium-webdriver
gem,如果您使用捆绑器,请将其添加到您的 Gemfile 中。
Capybara 预先注册了许多使用 Selenium 的命名驱动程序 - 它们是:
这些应该可以在本地桌面配置中使用(通过相关软件安装),但如果在 CI 环境中使用,您可能需要自定义它们,其中可能需要将其他选项传递到浏览器。请参阅有关添加和配置驱动程序的部分。
注意:在不同线程中运行服务器的驱动程序可能不会与您的测试共享相同的事务,从而导致测试和测试服务器之间无法共享数据,请参阅下面的事务和数据库设置。
完整的参考可以在 rubydoc.info 上找到。
注意:默认情况下,Capybara 只会定位可见元素。这是因为真实用户无法与不可见元素进行交互。
注意:Capybara 中的所有搜索均区分大小写。这是因为 Capybara 大量使用 XPath,而 XPath 不支持区分大小写。
您可以使用访问方法导航到其他页面:
visit ( '/projects' )
visit ( post_comments_path ( post ) )
访问方法仅采用单个参数,请求方法始终为 GET。
您可以获取浏览会话的当前路径,并使用have_current_path
匹配器对其进行测试:
expect ( page ) . to have_current_path ( post_comments_path ( post ) )
注意:您还可以通过直接测试current_path
的值来断言当前路径。但是,使用have_current_path
匹配器更安全,因为它使用 Capybara 的等待行为来确保前面的操作(例如click_link
)已完成。
完整参考:Capybara::Node::Actions
您可以通过以下链接和按钮与网络应用程序进行交互。 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
有许多用于与表单元素交互的工具:
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 ] }
如果您需要通过其他属性/属性查找元素,您还可以传递一个过滤器块,该过滤器块将在正常等待行为中进行检查。如果您发现自己需要经常使用它,那么最好添加自定义选择器或向现有选择器添加过滤器。
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' )
Capybara 可以将某些操作限制在页面的特定区域内,例如与表单交互或单击链接和按钮。为此,您可以使用通用的inside方法。您可以选择指定要使用的选择器类型。
within ( "li#employee" ) do
fill_in 'Name' , with : 'Jimmy'
end
within ( :xpath , ".//li[@id='employee']" ) do
fill_in 'Name' , with : 'Jimmy'
end
有一些特殊方法可以将范围限制为特定字段集(由字段集图例标记的 id 或文本标识)以及特定表(由表标题标记的 id 或文本标识)。
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 ( '[email protected]' )
find ( '#login_password' ) . set ( 'qwerty' )
click_button 'Submit'
end
在支持它的驱动程序中,您可以轻松执行 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
在支持它的驱动程序中,您可以接受、忽略和响应警报、确认和提示。
您可以通过将生成警报的代码包装在块中来接受警报消息:
accept_alert 'optional text or regex' do
click_link ( 'Show Alert' )
end
您也可以通过将确认包装在块中来接受或拒绝确认:
accept_confirm 'optional text' do
click_link ( 'Show Confirm' )
end
dismiss_confirm 'optional text' do
click_link ( 'Show Confirm' )
end
您也可以接受或忽略提示,还可以提供用于填写响应的文本:
accept_prompt ( 'optional text' , with : 'Linus Torvalds' ) do
click_link ( 'Show Prompt About Linux' )
end
dismiss_prompt ( 'optional text' ) do
click_link ( 'Show Prompt About Linux' )
end
所有模态方法都会返回所呈现的消息。因此,您可以通过将返回值分配给变量来访问提示消息:
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
的内容进行测试,而应使用更具表现力的查找器方法。
最后,在支持它的驱动程序中,您可以保存屏幕截图:
page . save_screenshot ( 'screenshot.png' )
或者让它保存并自动打开:
save_and_open_screenshot
屏幕截图保存到Capybara.save_path
,相对于应用程序目录。如果您需要capybara/rails
, Capybara.save_path
将默认为tmp/capybara
。
接受选择器的帮助器和匹配器共享一个通用的方法签名,其中包括:
这些参数通常以某种方式是可选的。
名称参数决定要使用的选择器。当助手显式传递选择器名称时,该参数是可选的(例如, find_field
使用:field
, find_link
使用:link
等):
page . html # => '<a href="/">Home</a>'
page . find ( :link ) == page . find_link
page . html # => '<input>'
page . find ( :field ) == page . find_field
定位器参数通常表示可以最有意义地区分与选择器匹配的元素和不匹配选择器的元素的信息:
page . html # => '<div id="greeting">Hello world</div>'
page . find ( :css , 'div' ) . text # => 'Hello world'
page . find ( :xpath , './/div' ) . text # => 'Hello world'
当该方法可以从Capybara.default_selector
配置推断默认值时,像find
和all
这样的通用查找器方法可以接受定位器作为其第一个位置参数: