Capybara: Ruby Integration & Acceptance Testing Guide

Capybara is a Ruby acceptance testing framework — drives a real browser via Selenium/Cuprite. Used with RSpec/Minitest for end-to-end Rails tests.

What is Capybara?

Capybara is a Ruby acceptance and integration testing library that simulates how a real user interacts with a web application. It provides a high-level DSL for visiting pages, filling forms, clicking links, and asserting on rendered content. Capybara is the de-facto standard for end-to-end testing in the Ruby/Rails ecosystem.

Capybara is a test driver, not a test framework — it works with RSpec, Minitest, or Cucumber. It supports multiple browser drivers: rack-test (no real browser, fast), Selenium (real Chrome/Firefox), Cuprite (Chrome via CDP), apparition (deprecated), and webkit (via capybara-webkit, also deprecated).

Why Capybara?

  • Reads like English. Tests describe user actions, not DOM internals.
  • Driver-agnostic. Same test runs against rack-test (fast) or Selenium (real browser).
  • Smart waiting. Auto-waits for elements to appear (no manual sleep).
  • Rails integration. Works out-of-box with Rails system tests (since Rails 5.1).
  • Mature ecosystem. Stable since 2009; battle-tested.

Basic Capybara test (with RSpec)

require 'rails_helper'

RSpec.feature 'User signup', type: :feature do
  scenario 'user signs up successfully' do
    visit '/signup'
    fill_in 'Email', with: 'alice@example.com'
    fill_in 'Password', with: 'secret123'
    click_button 'Create account'

    expect(page).to have_content 'Welcome, alice'
    expect(current_path).to eq '/dashboard'
  end
end

Common Capybara DSL methods

ActionMethod
Navigatevisit '/path'
Click link/buttonclick_link 'text' / click_button 'text'
Fill form fieldfill_in 'Label', with: 'value'
Select dropdownselect 'Option', from: 'Label'
Check checkboxcheck 'Label' / uncheck 'Label'
Choose radiochoose 'Label'
Attach fileattach_file 'Label', '/path/file.png'
Find elementfind('.selector') / find_by_id('id')
Assert contentexpect(page).to have_content('text')
Assert selectorexpect(page).to have_selector('.class')
Within scopewithin('.sidebar') { ... }

Capybara drivers compared

DriverReal browser?SpeedJS support?
rack-testNo (in-process)⚡ FastestNo
SeleniumYes (Chrome/Firefox)🐢 SlowFull
CupriteYes (Chrome via CDP)MediumFull
apparition (deprecated)YesMediumFull
capybara-webkit (deprecated)Yes (QtWebKit)MediumFull

Recommended: rack-test for non-JS tests, Cuprite for JS tests (faster + more stable than Selenium).

Configuring Capybara

# spec/rails_helper.rb
require 'capybara/rails'
require 'capybara/rspec'

Capybara.default_driver = :rack_test
Capybara.javascript_driver = :cuprite
Capybara.default_max_wait_time = 5  # seconds to wait for elements
Capybara.server = :puma, { Silent: true }

RSpec.configure do |config|
  config.before(:each, type: :feature, js: true) do
    Capybara.current_driver = :cuprite
  end
end

Capybara best practices

  • Use semantic selectors. fill_in 'Email', with: ... beats find('#user_email_field').set(...). Survives UI refactors.
  • Don't disable auto-wait. Avoid sleep; let Capybara wait for elements naturally.
  • Scope assertions. Use within '.modal' do ... end to avoid matching wrong elements.
  • Database cleanup. Use database_cleaner with truncation strategy for JS tests; transactions for non-JS.
  • Headless in CI. Run Chrome headless in CI for speed and stability.
  • Screenshots on failure. capybara-screenshot auto-captures on failure for debugging.
  • Page Objects. For complex apps, encapsulate selectors in Page Object classes (site_prism gem).
  • Avoid testing internals. Test what users see, not DOM structure.

Common pitfalls

  • Flaky tests. Most flake = race conditions. Use Capybara matchers (have_content, have_selector) which retry, not find().text == ... which doesn't.
  • Shared state across tests. Use database_cleaner; reset session.
  • JS not loading. Make sure you set js: true on the spec or feature.
  • Selenium hanging in CI. Use Cuprite — fewer hangs than ChromeDriver.
  • Different behavior in dev vs CI. Often headless vs headed; or different Chrome version.
  • Not testing real browser. rack-test misses JS-driven bugs entirely.

Capybara vs other tools

ToolLanguageApproach
CapybaraRubyDSL on top of Selenium/CDP drivers
CypressJSFull test runner; runs in-browser
PlaywrightJS/Python/Java/.NETCross-browser via CDP
Selenium WebDriverMulti-languageLower-level; more boilerplate
WatirRubySelenium wrapper; alternative to Capybara

FAQ: Capybara

Is Capybara still maintained?

Yes. Active development; widely used in Rails community. Latest major release: Capybara 3.x.

Should I use Capybara or Cypress?

Capybara if Ruby/Rails (lives in your test suite). Cypress if JS-only stack or you want a richer debugging UI.

What's the best driver for JS tests?

Cuprite. Faster + more reliable than Selenium ChromeDriver. Direct CDP connection skips WebDriver overhead.

How do I test JavaScript-driven UIs?

Tag specs with js: true and configure Capybara.javascript_driver = :cuprite.

Why are my Capybara tests flaky?

Usually race conditions or shared state. Use Capybara matchers (auto-retry); use database_cleaner for isolation; never sleep.

Can Capybara run in headless mode?

Yes — Cuprite runs headless by default; Selenium can be configured with headless: true.

How do I test file uploads?

attach_file 'File', Rails.root.join('spec/fixtures/test.png').

Load test your Capybara-tested app with LoadFocus

Capybara tests verify behavior; load tests verify scale. LoadFocus runs JMeter and k6 scripts against your Rails app from 25+ regions with up to 12,500 VUs. Sign up free at loadfocus.com/signup.

How fast is your website?

Elevate its speed and SEO seamlessly with our Free Speed Test.

Free Website Speed Test

Analyze your website's load speed and improve its performance with our free page speed checker.

×