The Basics of Capybara and Improving Your Tests

Share this article

Capybara in Love

Capybara is a web-based automation framework used for creating functional tests that simulate how users would interact with your application. Today, there are many alternatives for web-based automation tools, such as Selenium, Watir, Capybara, etc. All of these tools have the same purpose, but there are slight differences that make each of them more or less suitable.

The main characteristic that developers are aiming for is the ability to have tests that are modular, easy to write, and easy to maintain. This is especially true in Agile/TDD environments where writing tests is second nature. These tests are expected to give good and fast feedback on code quality. As time goes by, the number of tests grows and it can be a real nightmare to maintain the tests, especially when the tests are not modular and simple enough.

In this tutorial, I will describe some of Capybara key features and explain why it could be your tool of choice for developing web-based automated tests.

Anatomy of Capybara

Just like Watir, Capybara is a library/gem built to be used on top of an underlying web-based driver. It offers a user-friendly DSL (Domain Specific Language) which is used to describe actions that are executed by the underlying web driver.

When the page is loaded using the DSL (and underlying web driver), Capybara will try to locate the relevant element in the DOM (Document Object Model) and execute the action, such as click button, link, etc.

Here are some of the web drivers supported by Capybara:

  • rack::test: By default, it works with rack::test driver. This driver is considerably faster than other drivers, but it lacks JavaScript support and it cannot access HTTP resources outside of the application for which the tests are made (Rails app, Sinatra app).

  • selenium-webdriver: Capybara supports selenium-webdriver, which is mostly used in web-based automation frameworks. It supports JavaScript, can access HTTP resources outside of application, and can also be setup for testing in headless mode which is especially useful for CI scenarios. To setup usage of this driver you need to add: Capybara.default_driver = :selenium

  • capybara-webkit: For true headless testing with JavaScript support, we can use the capybara-webkit driver (gem). It uses QtWebKit and it is significantly faster than selenium as it does not load the entire browser. To setup usage of this driver, you need to add: Capybara.default_driver = :webkit

Installation

Capybara supports Ruby versions >= 1.9 and can be used in two modes: UI and headless.

In UI mode, add the following to the Gemfile:

gem install capybara
gem install #{web_driver_on_which_capybara_runs}

For Headless mode:

gem install capybara
gem install #{web_driver_on_which_capybara_runs}
apt-get install firefox
apt-get install xvfb
gem install selenium-webdriver # if using selenium-webdriver
gem install capybara-webkit # if using capybara-webkit
apt-get install libqtwebkit-dev # if using capybara-webkit

Basic DSL

Capybara comes with a very intuitive DSL which is used to express actions that will be executed. This is the list of basic command that are used:

visit('page_url') # navigate to page
click_link('id_of_link') # click link by id
click_link('link_text') # click link by link text
click_button('button_name') # fill text field
fill_in('First Name', :with => 'John') # choose radio button
choose('radio_button') # choose radio button
check('checkbox') # check in checkbox
uncheck('checkbox') # uncheck in checkbox
select('option', :from=>'select_box') # select from dropdown
attach_file('image', 'path_to_image') # upload file

Using the DSL, Capybara will search the following attributes in the DOM when trying to find an element:

  • Text field (fill_in): id, name, related label element
  • Click link/button (click_link/click_button): id, title, text within tag, value
  • Checkbox/radio button/dropdown (check/uncheck/choose/select): id, name, related label element
  • Upload file (attach_file): id, name

Advanced DSL

For scenarios where basic the DSL cannot help, we use xpath and CSS selectors (CSS selectors will be used by default). This is very common, since modern web applications usually have a lot of JavaScript code that generates HTML elements with attributes that have generated values (like random ids, etc.)

To find a specific element and click on it we can use:

find('xpath/css').click

To get text from specific element use:

find('xpath/css').text

To use xpath selectors, simply change the following configuration value:

Capybara.default_selector = :xpath

The selector type can be specified, if necessary:

find(:xpath, 'actual_xpath')

Matchers

When trying to find an element either using the DSL or xpath/CSS selectors, it is common to have two or more matches which will cause Capybara to fail with an Ambiguous match error. Also, Capybara can perform partial matches, leading to unexpected results. Because of that, it is important to be aware of matching strategies supported by Capybara:

  • :one – raises an error when more than one match found
  • :first – simply picks the first match – Don’t use this!
  • :prefer_exact – finds all matching elements, but will return only an exact match discarding other matches
  • :smart – depends on the value for Capybara.exact. If set to true, it will behave like :one. Otherwise, it will first search for exact matches. If multiple matches are found, an ambiguous exception is raised. If none are found, it will search for inexact matches and again raise an ambiguous exception when multiple inexact matches are found.

Scoping

To avoid unexpected behaviour (like ambiguous matches), it is a good practice to scope part of the DOM in which some action will be performed:

within(:xpath, 'actual_xpath') do
  fill_in 'Name', :with => 'John'
end

Capybara will first search the DOM with the xpath selector. Then, inside this part of the DOM, it will do an additional search for the text field.

Custom Selectors

It is also possible to define your own custom selectors. This can be very useful when we want to make modular selectors that can be reused in many scripts. Here is an example:

Capybara.add_selector(:my_selector) do
  xpath { "actual_xpath" }
end
find(:my_selector)

We can even pass arguments to the selectors (just like they are methods):

Capybara.add_selector(:my_selector) do
  xpath { |arg| "//xpath/#{arg}" }
end
find(:my_selector, arg)

or combine custom selectors with scoping:

Capybara.add_selector(:my_selector_area) do
  xpath { "actual_xpath" }
end

within(:my_selector_area) do
  fill_in 'Name', :with => 'John'
  fill_in 'Email', :with => 'john@doe.com'
end

Here are some best practices around Capybara DSL usage, custom selectors, and scoping and matching:

  • Scope part of the DOM on which action is performed.
  • Inside this scope prefer Capybara DSL when possible.
  • If it’s not possible, use relative xpath/CSS selectors within the scope.
  • xpath/CSS selectors should be as shallow as possible inside the scope.

JavaScript and Handling Asynchronous Calls

Capybara has an internal mechanism for handling asynchronous loading of parts of the page. A good example is clicking on some part of the page causing JavaScript to be executed that creates a new element on the page.

click_link('foo')
click_link('bar')

Here, let’s say clicking on the link ‘foo’ triggers an asynchronous process, like an Ajax request, that adds the link ‘bar’. It’s likely that second statement will fail since the link does not exist yet.

However, Capybara has a mechanism for handling these situations where it retries finding the element for a brief period of time before throwing an error. The default time is 2 seconds and can be changed with property:

Capybara.default_wait_time

There is no silver bullet for handling these cases. On one hand, it is a good practice to hide the complexity of the wait mechanism from the creator of the test script so the syntax is cleaner and easier to understand. On the other hand, sometimes it is better to have more control over the wait mechanism and handle it yourself.

In any case, this wait issue is often the cause of unexpected failures in the script. This is likely a good indicator that the implementation has either performance or other issues.

Capybara also supports execution of JavaScript via:

page.execute_script()

where JavaScript code can be passed like this:

page.execute_script("$('#area button.primary').click()")

Using Capybara with RSpec

Capybara naturally fits with RSpec and no special integration is needed.

describe "Create place scenario" do
  context "Go to home page" do
    it "opens homepage" do
      visit(get_homepage)
    end
  end
  context "Click on create object link" do
    it "opens create new object form" do
      find(:homepage_navigation_create_object).click
    end
  end
end

So far, we have seen how we can use the DSL to interact with page elements, but there is one caveat: With every new context, a new Capybara session will be instantiated which is probably not what we want. Capybara::Session.new :driver helps manage this issue. For example:

session = Capybara::Session.new :selenium # instantiate new session object
session.visit() # use it to call DSL methods

The session represents a single user interaction with the application, which is precisely what we want.

Good practice: Define a method in spec_helper.rb which will return the session object and use this session object when calling all Capybara methods inside RSpec context (session.visit, session.find, etc.)

Debugging

There are couple of Capybara debugging methods, like:

save_and_open_page # saves current snapshot of page
print page.html # retrieve current state of DOM
save_sceenshot('screenshot.png') # save screenshot

There is another method, which I think is more useful for debugging: the pry and pry-debugger gems.

These gems will let you inspect script execution in an actual debugger, allowing you to set breakpoints, go next, step into, continue, etc. To use them, do the following:

  • gem install pry-debugger (automatically installs pry as well)
  • require 'pry' in your test script or spec helper
  • add binding.pry to the script to set a breakpoint and then, execute the test script. Execution will paus on the breakpoint. At this point, use next (to go to next line), step (to step into definition), continue (to continue execution) to debug as necessary.

For more info about these gems check: * Pry * Pry-debugger

Wrapping Up

I’ve briefly run through the basic of Capybara and how to customize it to meet your needs. Testing is a way of like in the Ruby community, and Capybara is one of the tools of choice. Test well, my friends.

Frequently Asked Questions (FAQs) about Capybara Testing

What is Capybara and why is it important in web development?

Capybara is a web-based test automation software that simulates scenarios for user stories and automates web application testing for behavior-driven software development. It is a part of the Cucumber testing framework written in the Ruby programming language. Capybara can mimic actions of real users interacting with web-based applications, making it an essential tool in web development. It helps developers to ensure that their applications are functioning as expected before they are deployed.

How does Capybara improve the quality of tests?

Capybara enhances the quality of tests by providing a high-level API to allow developers to interact with their web applications. It supports multiple web drivers and various browsers, including headless browsers. This means that it can simulate user interactions in a way that is independent of the underlying driver or browser, leading to more robust and reliable tests. Capybara also waits for elements to appear on the page, reducing the likelihood of timing errors in your tests.

Can Capybara be used with other testing frameworks?

Yes, Capybara is not just limited to Cucumber and can be used with RSpec or any other Ruby-based test frameworks. This flexibility allows developers to choose the testing framework that best suits their project requirements and personal preferences.

How does Capybara handle asynchronous JavaScript?

Capybara has built-in support for asynchronous JavaScript, meaning it can handle scenarios where elements on the page might not be immediately available. It does this by automatically waiting for a certain amount of time for AJAX and JavaScript events to complete before failing the test. This feature makes Capybara particularly useful for testing modern, dynamic web applications.

What are the different drivers that Capybara supports?

Capybara supports a variety of drivers such as RackTest, Selenium, and Capybara-WebKit, among others. Each driver has its own strengths and weaknesses, and the choice of driver depends on the specific needs of your test suite. For example, RackTest is fast but does not support JavaScript, while Selenium supports JavaScript but is slower than RackTest.

How can I customize the wait time in Capybara?

Capybara allows you to customize the maximum wait time for asynchronous processes. This can be done by setting the ‘Capybara.default_max_wait_time’ option to the desired number of seconds. This feature is particularly useful when dealing with tests that may require more time to pass, such as those involving complex AJAX requests.

Can Capybara be used for testing mobile applications?

While Capybara is primarily used for testing web applications, it can also be used for testing mobile applications using tools like Appium. However, it’s important to note that Capybara was not specifically designed for mobile testing, and other tools might be more suitable for this purpose.

How does Capybara compare to other testing tools?

Capybara stands out from other testing tools due to its robustness and flexibility. It supports multiple web drivers, has built-in wait strategies to handle asynchronous loading, and provides a high-level API to interact with web applications. However, like any tool, it may not be the best fit for every project, and the choice of testing tool should be based on the specific needs of the project.

Can Capybara tests be run in parallel?

Yes, Capybara tests can be run in parallel to speed up the overall test suite. This can be achieved using tools like Parallel Tests gem. However, running tests in parallel can introduce its own set of challenges, such as managing test data and dealing with race conditions, and should be done with care.

How can I debug Capybara tests?

Capybara provides several methods to aid in debugging. For example, you can use the ‘save_and_open_page’ method to save the current page and open it in your default browser. You can also use the ‘pry’ gem to set breakpoints in your code and inspect the state of your test at any point.

Bakir JusufbegovicBakir Jusufbegovic
View Author

IT professional with 4+ years experience in software automation and testing who is currently employed in AtlantBH. Always willing to learn new technologies and improve my skills as well as improve existing software solutions on which I'm working

GlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week