Sinatra
Capybara is a ruby framework for remotely controlling a browser by performing actions like visiting websites, clicking links and filling out forms procedurally. This framework can be leveraged in combination with RSpec to create a way of testing that your webpages work correctly, for example, checking that links click through correctly and forms display as you intend.
Start Up
Install the capybara base code by using gem 'capybara' inside your Gemfile with the bundle command. Next run an rspec --init command to initialise your spec directory and spec_helper.rb file. You then need to edit your spec_helper file to with some configuration settings to point it towards the correct class / file, as well as require the RSpec implementation of capybara.
# add this to spec/spec_helper.rb
ENV['RACK_ENV'] = 'test'
# require our Sinatra app file
require File.join(File.dirname(__FILE__), '..', 'app.rb')
require 'capybara'
require 'capybara/rspec'
require 'rspec'
# tell Capybara about our app class
Capybara.app = Battle
Tests that use capybara should be placed in the spec/features directory. These tests are then run automatically when you use the rspec command to trigger all your tests.
Structure
The basic structure of capybara tests is similar to RSpec core with slightly different adjectives. In Capybara we run a feature block instead of a describe block to indicate which broad feature we are testing and we run a scenario block instead of an it block. You can then do set up using basic capybara and the vanilla RSpec expect adjective with capybara commands.
feature 'Tests web page content' do
scenario 'It has correct text' do
visit('/')
expect(page).to have_content 'Hello world!'
end
end
The page variable is the equivalent of subject in capybara and points to the content on whatever page the test is currently running.
Capybara will raise a confusing XPath error that reads unable to find xpath "/html" if you try to test a page which is entirely empty. Be wary of this!
Or And
Capybara does not support conjunction operators like or or and from regular RSpec its expect blocks.
# both of these are NOT VALID
expect(page).to have_content('this').and have_content('that')
expect(page).to have_content('this').or have_content('that')
Helpers
You can define reusable pieces of code for your Capybara tests to help keep your test code dry using helper methods. These are essentially methods defined in a separate file that can be called in your capybara tests to execute code that is used multiple times. To create a capybara test helper:
- Create a file in your projects
spec/featuresdirectory. It can be called something likehelpers.rb, but the name doesn’t actually matter. - Add a
require helpers.rbline to yourspec_helperfile. - In the
helpers.rbfile define a method that implements the duplicated capybara code you want to extract. - Replace the duplicated code in your main capybara tests with the method from
helpers.rb.
Your helpers.rb file might look like this:
def visit_page_and_sign_in
visit('/')
fill_in :player_1_name, with: 'Dave'
fill_in :player_2_name, with: 'Mittens'
click_button 'Submit'
end
Now in the main tests you simply replace these lines with visit_page_and_sign_in methods in your main test. Any future tests we write can used this extracted code easily to sign in one line.
feature 'has sign in confirmation' do
scenario 'signs in' do
visit_page_and_sign_in
expect(page).to have_content('sign in sucessful')
end
end
Commands
Finders
visit('/url-extension') loads whatever page you want to test.
You can find an object by its ID value using the find_by_id method. This can then have other actions appended to it.
find_by_id('my button').click
You can find a specific option within an element by using the find method combined with the :xpath option and an identifier. The query in the find should be submitted as an xpath identifier.
# finds the second option in a drop down menu
find_by_id('my dropdown').find(:xpath, 'option[2]')
Actions
To click a button use the click_button command. You can also use the click command appended to a query that finds an element in your HTML document.
# clicks 'my button'
click_button('my button')
# find the 'my button' element and click it
find('my button').click
You can fill in a form field using the fill_in command, combined with the field name, id or label and the value to add.
# fills in the name field of a form with "John"
fill_in('name', with: 'John')
To select an option from a dropdown use the select_option action. Un-intuitively you cannot use the click selector for selecting an option from a drop down.
# finds the sencond option of the drop down menu and selects it
find_by_id('my dropdown').find(:xpath, 'option[2]').select_option
To select a radio button use the choose action. This clicks a radio button based on its name.
# chooses a gender identity from a radio button list
choose('male')
click_button('submit')
Randomness
Like the RSpec, Capybara supports use of the srand object to get consistent random behavior. Because you can’t directly mock outputs in Capybara easily using srand can be give you consistent outputs.
Matchers
Most capybara matchers for testing page elements work by using the name, id or type values on an HTML element. You should avoid using the CSS selectors to test elements on the page.
You can test a page has a specific piece of text or content by using the have_content(content) matcher which returns true if a page contains the content submitted in the argument.
expect(page).to have_content('Hello world!')
You can check for form content elements using the have_field matcher. This checks based id component of the HTML <form> field object.
expect(page).to have_field('name')
You can test that a link or button goes to a the correct URL by using the have_current_path matcher which test the current URL page extension. In the example below we test a button that takes us to the /about page.
click_button('some link')
expect(page).to have_current_path('/about')
Orderly
Orderly provides a simple custom matcher for checking that page one page element appears before another. You can install Orderly by adding the gem 'orderly' to your gemfile.
To test if one element appears before another select an element using regular Capybara and then assert a test with the appear_before matcher.
# find elements on the page
this = find('text_area_1')
that = find('text_area_2')
expect(this).to appear_before(that)