Feature Testing Using Capybara

Feature testing is high level testing that allows us to go through our entire system, ensuring that each and every component is working perfectly fine. The test-cases written for it are termed as feature specs, which requires capybara.

In this blog, we will be focussing on writing feature specs using capybara with rspec. More specifically, we are going to look at capybara key features as well as, how capybara specs looks like.

We as a developer are mainly focussed about the underlying things, but we should also consider end user’s view. So, Capybara comes to rescue, it lets you write yours test-cases from user’s perceptive i.e, how end users would interact with your system. It is a ruby gem build on the top of underlying web-based driver and these drivers drive our browsers based on what our master ‘capybara’ says.

There are multiple web-drivers supported by capybara, some of these are as follows:

  • rack:test

    Capybara supports this web-driver by default. It is a library that sits above the rack app, and allows to fake web-requests to the app as a result making it considerably faster than other web-drivers because in reality we haven’t started a server. But it lacks JavaScript support.

  • selenium-webdriver

    It is one of the most popular driver and by default it supports Firefox browser where we can actually visualize our tests. It uses browser’s Javascipt Engine making it feel like having a actual human Q/A department interacting with our system. Also, we can configure it to be used in headless-mode which is specifically good from time and CI perspective.

  • capybara-webkit

    This one is comparatively faster than selenium-webdriver as it doesn’t load our entire app and also supports JavaScript, making it useful for true headless testing. It depends on Qt, a cross platform development toolkit. For more information, you can refer this capybara-webkit and Qt.

  • poltergeist

    This one runs our test-cases in PhantomJS browser with full JavaScipt support. It decreases the overall testing time by reducing the number of dependencies needed by CI server. But to use this, our system must have PhantomJS installed. Also, TravisCI, CircleCI have PhantomJS preinstalled. For more information you can refer poltergeist and phantomjs.

Setup

Provided you have installed Ruby, Rails and Rspec, we have to install Capybara. Here we will be using poltergeist driver which depends on PhantomJS, so firstly we need to have PhantomJS.

  • Installing PhantomJS

    Homebrew::

     brew install phantomjs
    

    MacPorts::

     sudo port install phantomjs
    
  • Installing Capybara and Poltergeist

    Add this to Gemfile and run bundle install.

     #Gemfile
    
     ...
    
     gem 'capybara'
     gem 'poltergeist'
    

    After installing it, we need to tell our RSpec that we want to use Capybara with Poltergeist by adding this to spec/rails_helper.rb:

     #spec/rails_helper.rb
     
     ....
     
     # load up Capybara
     require 'capybara/rspec'
     require 'capybara/rails'
    
     # load up Poltergeist
     require 'capybara/poltergeist'
     #Capybara.javascript_driver = :other_web_driver
     Capybara.javascript_driver = :poltergeist  
    

    If we want to use web-driver other than poltergeist, we can include that here in place of poltergeist in the above mentioned file. But if we do not specify any capybara driver, than by default it will use rack::test.

Application Example

As we have already installed everything, lets move onto how to write feature specs using capybara?

Consider a scenario, where our app has a home page and on that page we have a Log in button, so when a already registered user clicks on that Log in button she/he should be taken to a dashboard page.

Let’s test this feature using capybara:

require 'rails_helper'

RSpec.feature "Log In" do
  background do
    @user = create :user, email: 'test@gmail.com'
  end

  scenario "signing in with valid credentials", js: true do
    visit '/homes'
    
    expect(page).to have_link('Log in')
    
    within(".login") do
      click_link 'Log in'
    end

    within(".sign-up") do
      fill_in 'Email', with: @user.email
      fill_in 'Password', with: 'testpassword'
      click_button 'Log In'
    end
    expect(page).to have_text('Dashboard page')
  end
end

Here, we have used js:true to tell capybara that it has to use JavaScript driver for this test-case, otherwise if we don’t mention, it will by default use rack::test

One of the problem that I have faced when using Capybara with Rspec is that sometimes my test-cases were passing, while other times they were failing. So after searching I found that the main cause was because when we have feature spec with Capybara’s JavaScript driver, in that case the app-under-test and specs does not share a database connection. So, the solution is to use DatabaseCleaner truncation strategy instead of transaction one.

In order to use DatabaseCleaner, we have to include this in our Gemfile and run bundle install:

#Gemfile

....

group :test do
 gem 'database_cleaner'
end

After that, we have to configure our DatabaseCleaner strategy in spec/rails_helper.rb:

#spec/rails_helper.rb
RSpec.configure do |config|
  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.strategy = :truncation

    # Start transaction
    DatabaseCleaner.cleaning do

      # Run example
      example.run
    end
  end
end

There are many ways as well as frameworks for doing end-to-end testing, one is using capybara with rspec which we have already covered in this blog. We can also do this testing using minitest that I’ll be covering in my next blog. Stay tuned 🙂

Advertisements