The DSL, Drivers & Automatic Waiting
Testing Like a Real User
Capybara is a Ruby library for integration/acceptance/feature tests — verifying an entire user-facing flow ("a user can sign up and log in") by simulating actual browser interactions, rather than testing an isolated unit of code. Its DSL reads like a narrative of user behavior, not low-level DOM manipulation.
A Feature Test
visit '/login'
fill_in 'Email', with: 'user@example.com'
fill_in 'Password', with: 'secret'
click_button 'Log in'
expect(page).to have_content('Welcome back')visit navigates to a page; fill_in, click_button, click_link simulate the specific actions a real user would take. have_content is an RSpec matcher integrated with Capybara's waiting mechanism, retrying until the assertion passes or a timeout is reached.
Automatic Waiting — No Manual Sleeps
Since modern pages update asynchronously (AJAX, JS), an element might not be present the instant a test checks for it. Capybara's finders retry automatically for a configurable timeout instead of failing immediately — sparing test authors from the classic anti-pattern of fixed sleep calls, which either waste time waiting too long or cause flaky failures by not waiting long enough.
Drivers: Speed vs. JS Fidelity
Rack::Test (the default driver) simulates HTTP requests directly against the app with no real browser — fast, but can't execute JavaScript. A JS-capable driver (Selenium, or a headless Chrome-based driver like Cuprite) is needed when the feature under test depends on client-side JS behavior — a dynamic section, an AJAX call, a JS-driven modal. Common practice: default to Rack::Test, opt into a JS driver (often via a js: true tag) only when actually needed.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free