Over a million developers have joined DZone.

Capybara and Selenium for Testing and Scraping

Capybara is a great tool to have in your Ruby toolbelt. Give it a try the next time you want to make sure that a certain key user flow works or you need to automate a task via scraping.

· DevOps Zone

The DevOps zone is brought to you in partnership with Sonatype Nexus. The Nexus suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

Capybara, aside from being the largest rodent in the world, is also a fantastic tool to aid you in interacting with browser functionality in your code, either for testing or just to interact with or scrape data from a website.

Capybara isn’t what actually interacts with the website—rather, it’s a layer that sits between you and the actual web driver. This could be Selenium, PhantomJS, or any of the other drivers that Capybara supports. It provides a common interface and a large number of helper methods for extracting information, inputting data, testing, or clicking around.

Just like any abstraction, sometimes you need to go deeper, and Capybara won’t stop you from doing that. You can easily bypass it to get at the underlying drivers if you need more fine-tuned functionality.

Testing With Capybara

Capybara integrates really nicely with all of the common test frameworks used with Rails. It has extensions for RSpec, Cucumber, Test::Unit, and Minitest. It’s used mostly with integration (or feature) tests, which test not so much a single piece of functionality but rather an entire user flow.

You can use Capybara to test whether certain content exists on the page or to input data into a form and then submit it. This is where you try to ensure that the same key flows (such as registration, checkout, etc.) that your user will take work not just in isolation but flow nicely from one to another.

With RSpec, we need to first ensure that in our rspec_helper.rb file we include the line require 'capybara/rails'. Next, let’s create a new folder called features where we’ll put all of the tests which include Capybara.

Imagine that we have an application for managing coffee farms. In this application, creating a coffee farm is one of the most important functions you can perform, and therefore should be tested thoroughly.

# spec/features/creating_farm_spec.rb
require 'rails_helper'

RSpec.describe 'creating a farm', type: :feature do
  it 'successfully creates farm' do
    visit '/farms'
    click_link 'New Farm'

    within '#new_farm' do
      fill_in 'Acres', with: 10
      fill_in 'Name', with: 'Castillo Andino'
      fill_in 'Owner', with: 'Albita'
      fill_in 'Address', with: 'Andes, Colombia'
      fill_in 'Varieties', with: 'Colombia, Geisha, Bourbon'
      fill_in 'Rating', with: 10
    click_button 'Create Farm'

    expect(page).to have_content 'Farm was successfully created.'
    expect(page).to have_content 'Castillo Andino'

There are a few things to note with Capybara. The first is that it provides a ton of great helpers such as: click_link, fill_in, click_button, etc. Many of these helpers provide a variety of ways to actually find the HTML element that you’re looking for.

In the example above, we see CSS selectors used with the within method. We also see selecting and filling in an input field by using the text in its label.

There’s also a third way, not shown here, which allows you to select elements using xpath. While xpath is the most powerful for selecting, it’s the least clear way. For the purposes of your own sanity, you should probably aim to include an ID or class property in your HTML to ensure that selecting is straightforward.

Scraping With Capybara

Capybara isn’t only for testing. It can also be used in web scraping. I’ll admit that it isn’t the fastest method, and if all you are doing is visiting a page to extract information without too much interaction with the DOM in terms of data input or clicking, it may not be the best approach. For that, you may want to investigate something like mechanize or even nokogiri if all you are doing is reading HTML to extract information from it.

But for the situation where you maybe have to first log in as a user, click on a tab, and then extract some information, this is the sweet spot for Capybara.

I’ve recently had to rent a car, and I ended up using Hotwire for this. Let’s use Capybara to log in and retrieve my confirmation number. In this case, it would be more difficult to use a different scraping tool because it is an Angular SPA, so Capybara works perfectly.

I’ll create a Rake task which will log into my account and then loop through all of the confirmation codes and print them to the screen. I’ve used an xpath selector here to show that even if there isn’t an easy CSS selector to use, you can still find the element that you’re looking for. This also demonstrates how to use Capybara outside of your testing environment.

namespace :automate do
  desc 'Grab hotwire confirmation code'
  task hotwire: :environment do |t, args|
    session = Capybara::Session.new(:selenium)
    session.visit 'https://www.hotwire.com/checkout/#!/account/login'

    session.find(:xpath, '//input[@type="password"]').set(ENV.fetch('PASSWORD'))

    session.all('.confirmation-code').each do |code|
      puts code.text

On the screen, we get Car confirmation 31233321CA3 outputted (not my real confirmation number, of course).

Any time we use the find method or all, we are given an instance of the Capybara::Node::Element object. This object allows us to click it, extract the text, ask for its DOM path, and interact with it in a variety of other ways.

One other interesting method is the native method, which returns us the underlying driver’s object. In this case, it’s an instance of Selenium::WebDriver::Element because we are using Selenium. As useful of an abstraction as Capybara is, there will always be times when you need to gain access to the underlying layer.

As you can see, this could be an easy way to automate a task that has no other alternative than to use the “Screen Scraping” approach. Keep in mind that this is quite brittle, as a slight change to one of their classes or IDs means that the whole thing will stop working.

Interacting With JavaScript

One of the things that Capybara gives you is the ability to interact with your webpages using JavaScript. You aren’t limited to only using Ruby to find and interact with the DOM nodes on your page.

Capybara gives you two methods to invoke some JavaScript:

  • execute_script (does not return values)
  • evaluate_script (does return values)

These work great, but as usual you can bypass Capybara if needed and use the underlying driver to execute JavaScript. This allows us to pass arguments to our JavaScript code:

# example of returning values from javascript
classes = session.driver.browser.execute_script(
  "return document.getElementById(arguments[0]).className;",
puts classes
# => ng-pristine ng-untouched ng-invalid ng-invalid-required

The execute_script method allows you to return values from JS functions which are called. If you imagine that your code is being invoked like this:

var result =
  (function(arguments) {
    return document.getElementById(arguments[0]).className;

You’ll see that the arguments you pass in the second and higher parameter positions get placed into an array and passed to an anonymous function. The anonymous function contains, as its body, the code which was in the first parameter. This is why you must explicitly include a return statement if you want to use the value it returns.

Selenium takes care of how to convert what would end up being a JavaScript return value into something you can use in Ruby.

Configuring Capybara

As I mentioned in the introduction, Capybara works by allowing you to work with a number of different web drivers. These could be lightweight headless drivers such as PhantomJS (via poltergeist) or RackTest, but it could also be Selenium either running locally or connecting to a Selenium grid server remotely.

Here is an example of how you might configure Capybara to work with a remote Selenium server.

Capybara.register_driver(:firefox) do |app|
    browser: :remote,
    url: ENV.fetch('SELENIUM_URL'),
    desired_capabilities: Selenium::WebDriver::Remote::Capabilities.firefox

Which would now allow you to set the driver to :firefox with the code Capybara.default_driver = :firefox.

Debugging Capybara

With a headless driver, it is sometimes hard to see what the page looks like at the time you are interacting with it. Just because it is headless doesn’t mean you need to be blind. You are able to request a screenshot of how the page looks and also extract the source code of the page.

File.open('/tmp/source.html', 'w') do |file|
  io = StringIO.new(session.driver.browser.page_source)

File.open('/tmp/screenshot.png', 'w', encoding: 'ascii-8bit') do |file|
  io = StringIO.new(session.driver.browser.screenshot_as(:png))

Another way to help with debugging is by placing a binding.pry call, which will pause the script and allow you to step into the code and perform commands that interact with the web page. You can even open up the Firefox/Chrome developer console and play with the JavaScript of the page in its current state.


The truth is that I don’t generally use Capybara when testing my Rails applications. It slows the tests down and potentially binds your tests quite closely to the DOM.

However, it does have its place, especially when you need to guarantee that certain important user flows work exactly as expected. It also finds the odd use in scraping when tools such as mechanize or nokogiri aren’t enough.

Capybara is a great tool to have in your Ruby toolbelt. Give it a try the next time you want to make sure that a certain key user flow works or you need to automate a task via scraping.

The DevOps zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today


Published at DZone with permission of Leigh Halliday, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}