Capybara 入门:模拟真实用户的 Ruby 集成测试框架
Capybara 是 Ruby 集成测试的事实标准,用真实浏览器模拟用户操作。本文讲解驱动架构(RackTest/Selenium)、元素定位、交互 API 与异步等待机制的正确用法。
Capybara 通过模拟用户在浏览器中的真实行为(访问页面、点击、填表单、断言内容)来做集成/验收测试。默认 RackTest 驱动速度快但不跑 JS,需要 JavaScript 的场景切 Selenium + Chrome;它内置自动等待机制,绝大多数"异步元素找不到"的问题源于没用对查找方法。
为什么需要 Capybara
单元测试验证单个对象,请求测试验证接口响应,但"用户注册→收邮件→激活→登录"这条链路的真实体验,只有驱动浏览器走一遍才能确认。Capybara 就是 Ruby 生态做这件事的标准答案——Rails 系统测试默认搭载它,Sinatra/Hanami 等项目也能直接用。
驱动架构:按需切换
Capybara 的 API 与底层驱动解耦:
- RackTest(默认):不启真实浏览器,直接在 Rack 层模拟,速度极快,适合无 JS 的页面;
- Selenium + Chrome/Firefox:真实浏览器,支持 JavaScript,配
driven_by :selenium, using: :headless_chrome无头运行; - Apparition/Cuprite:基于 Chrome DevTools 协议,比 Selenium 更稳更快的新兴选择。
Rails 项目配置示例:
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 900]
end
核心 API:像用户一样操作
visit '/login'
fill_in '邮箱', with: 'user@example.com'
fill_in '密码', with: 'secret'
click_button '登录'
expect(page).to have_content('欢迎回来')
expect(page).to have_current_path('/dashboard')
within('.sidebar') { click_link '设置' } # 限定作用域
find('.modal').click # 显式定位元素
all('.item').count # 批量元素
定位优先用语义化方式(按钮文本、label、role),find('#id') 这类实现细节定位最脆弱——UI 改版时测试会成片倒下。
异步等待:Capybara 的隐藏超能力
have_content、find 等断言自带重试:默认最长等待 2 秒(Capybara.default_max_wait_time),元素出现即通过。这解决了前端渲染、Ajax 加载的时序问题——前提是你要用对方法:
# 错误:立即求值,异步内容未到就失败
expect(page.text).to include('加载完成')
# 正确:Capybara 自动等待
expect(page).to have_content('加载完成')
# 等待消失同样重要(如 loading 遮罩)
expect(page).to have_no_content('加载中')
绝不要写 sleep 3——那既慢又不可靠,所有 sleep 都能换成 have_* 等待断言。
常见问题(FAQ)
Q:测试在 CI 里随机失败(flaky)怎么办?
九成是等待问题:检查是否绕开了 Capybara 的等待机制(直接访问 page.text、用 first 而非 find);CI 上适当调大 default_max_wait_time 到 5 秒;截图工具(save_screenshot)在失败时自动留存现场。
Q:怎么测试文件上传和下载?
上传用 attach_file('头像', 'spec/fixtures/avatar.png');下载在 headless Chrome 配置下载目录后用文件存在性断言。弹窗(confirm/alert)用 accept_confirm { click_link '删除' } 包裹。
Q:系统测试太慢,怎么控制数量?
系统测试只守"黄金路径"(核心业务旅程),细粒度逻辑下沉到请求测试和单元测试——测试金字塔依然成立。大套件可以并行(parallel_tests gem)并给 JS 驱动用 Cuprite 提速。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。