Capybara

"Acceptance test framework for web applications"

Compiegne.rb - 2013-01-24

Philippe Lafoucrière / Tech-Angels / @plafoucriere

What is Capybara?

From Capybara homepage:

Capybara helps you test web applications by simulating how a real user would interact with your app. It is agnostic about the driver running your tests.

=> Capybara is an abstraction layer between a Rack app and testing webdrivers.

Developed by Jonas Nicklas / @jonicklas / Github

Testing like a boss

Jonas Nicklas

Jonas Nicklas is also the author of:

One of the six 2011 Ruby Hero Award Winners.

Testing your app

Testing apps has become one the most discussed subject in the last years. Testing is fundamental because:

  • Your app will probably grow instead of getting thiner
  • Changing code will certainly cause side effects you didn't expect
  • Refactoring is easier
  • Maintenance is easier
  • it helps understanding the behavior of the app

Different layers

  • Unit Testing
  • Functional Testing
  • Acceptance Testing
  • Integration Testing

Supported Drivers

  • RackTest (default, no js)
  • Selenium 2.0
  • Webkit (headless)
  • Poltergeist (headless)

Getting Started

Add capybara to your Gemfile and bundle it:


  group :test do
    gem 'rspec'
    gem 'capybara'
  end
            
Require capybara in your spec helper:

  require 'capybara/rails'
  require 'capybara/rspec'
            
Put your specs inside spec/features

Write your first scenario


# spec/features/sign-in_spec.rb
feature "Signing in" do
  background do
    @user = create :user, password: 'MyNameIsBond' # FactoryGirl Syntax
  end

  scenario "Signing in with correct credentials" do
    visit '/users/sign_in'
    within("#new_session") do
      fill_in 'Nickname or email', with: @user.email
      fill_in 'Password', :with => with: @user.password
    end
    click_button 'Sign in'
    page.should have_content 'Success'
  end
end
              

DSL - Navigating


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

DSL - Links and buttons


  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')
              

DSL - Interacting with forms


  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')
              

DSL - Querying DOM


  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')
              

DSL - Scoping


  within "table.companies" do
    find(:xpath, "//a[@href='/companies/#{@company.id}']").click
  end
              

DSL - Scripting


  page.execute_script("$('body').empty()")
  result = page.evaluate_script('4 + 4');
              

Testing Ajax

That's the beauty of Capybara, just use the right driver! Capybara-Webkit is playing very nice with capybara, let's use it for our examples.

To start testing Ajax behaviours, simply add the webkit driver. First, let's add it to our Gemfile:


    group :test do
      gem 'rspec'
      gem 'capybara'
      gem 'capybara-webkit'
    end
             

Testing Ajax

Tell capybara to use the webkit driver when testing JS behaviour.


  # spec_helper.rb

  RSpec.configure do |config|
    # [...]
    Capybara.javascript_driver = :webkit
  end
             

Testing Ajax

Tell rspec to use the javascript_driver instead of the default_driver.


describe 'admin/users', js: true do

  context "index" do
    before { admin_login }
    before { @user = create :user }

    it "should display user" do
      visit '/admin/users'
      within("#main_content") { click_on "View" } # <== Toggle a div
      page.should have_content "User Details"
      page.should have_content @user.email
    end
  end
end
             

Now the bad news... Transactions

Some drivers (like webkit) will run a server on a separate thread. Therefore, transactions aren't "visible" in another context (thread). This will cause some tests to fail with a RecordNotFound Exception

Short answer: like cucumber, you must use database_cleaner instead of rspec transactions.

Going further

Another very nice project from Jonas Nicklas is Turnip. Turnip enables the use of Gherkin (the cucumber syntax) directly in rspec, without having a dedicated env.

Turnip is using steps like cucumber, and can (and should) use capybara DSL:


  step "I should be on the homepage" do
    page.current_path.should == root_path
  end

  step "I should see the company :name in the table" do |name|
    page.find('table.companies').should have_content name
  end