Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Testing front-end for a Sinatra app with RSpec and Capybara

Testing front-end for a Sinatra app with RSpec and Capybara

I've used Cucumber quite a bit on my last job. It's an excellent tool, and I believe readable tests are the way to the future. But I could never get around to write effective scenarios, or maintain the boatload of text that the suite becomes once you get to a point where you have decent coverage. On top of that, it didn't seem to take much for the suite to become really slow as tests were added.

A while ago I've seen a gist by Lachie Cox where he shows how to use RSpec and Capybara to do front-end tests. That sounded perfect for me. I love RSpec, I can write my own matchers when I need them with little code, and it reads damn nicely.

So for my Rails Rumble 2010 project, as usual, I rolled a Sinatra app and figured I should give the idea a shot. Below are my findings.


Starting with the Gemfile, just so you can see what gems I'm using for this exactly.

source :rubygems

gem 'sinatra',      '1.0'
gem 'thin',         '1.2.7'
gem 'mongoid',      '2.0.0.beta.19'
gem 'bson_ext',     '1.1.1'
gem 'haml',         '3.0.21'
gem 'bcrypt-ruby',  '2.1.2', :require => 'bcrypt'
gem 'json_pure',    '1.4.6', :require => 'json/pure'

group :development do
  gem 'sinatra-reloader', '0.5.0'

group :test do
  gem 'rspec',            '2.0.0'
  gem 'faker',            '0.3.1'
  gem 'machinist',        '2.0.0.beta2'
  gem 'machinist_mongo',
    :require  => 'machinist/mongoid', 
    :git      => 'git://',
    :branch   => 'machinist2'
  gem 'capybara',         '0.3.9'


Location: ROOT/Rakefile.

So we can run "rake spec" and get the suite to run.

require 'rubygems'
require 'rspec/core/rake_task' do |task|
  task.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
  task.pattern    = 'spec/**/*_spec.rb'


Location: ROOT/spec/spec_helper.rb.

This is the file that bootstraps the testing environment. Every spec will require this first.

require 'bundler/setup'
require 'sinatra'
Sinatra::Application.environment = :test
Bundler.require :default, Sinatra::Application.environment
require 'rspec'
require 'machinist'
require 'machinist/mongoid'
require File.dirname(__FILE__) + '/../config/boot'

RSpec.configure do |config|
  config.before(:each) { Machinist.reset_before_test }

Store.blueprint do
  name        { }
  email       { }
  password    { 'test123' }
  description { Faker::Lorem.paragraph(1 + rand(3)) }
  location    { Faker::Address.street_address }


Location: ROOT/spec/acceptance_helper.rb.

Loads the Sinatra app file itself (the one where your routes are), Capybara, and it also features some helpers which we'll use on the front-end specs, both to improve readability and keep them short.

require File.dirname(__FILE__) + '/spec_helper'
require Sinatra::Application.root + '/app'
disable :run

require 'capybara'
require 'capybara/dsl' = Sinatra::Application

RSpec.configure do |config|
  config.include Capybara

# Helpers
def signup store
  visit '/signup'
  fill_in 'email',  :with =>
  fill_in 'name',   :with =>
  fill_in 'description',  :with => store.description
  fill_in 'location',     :with => store.location
  click 'Create my account'

def signin email, password
  visit '/'
  # within('#sign-in') do
    fill_in 'email', :with => email
    fill_in 'password', :with => password
    click 'Sign in'
  # end

def selector string
  find :css, string

The specs

I've placed all my front-end specs in ROOT/spec/acceptance. I would run them individually through the TextMate bundle at all times, so I had no need to make a separate rake task just for that.

Plus on deploy I wanted to make sure that everything passed, so having those get picked up by "rake spec" was handy.

Sample: login_required_spec.rb

Location: ROOT/spec/acceptance/login_required_spec.rb.

A sample spec that ensures that certain pages that are password protected will, upon being visited without a valid session, direct the user to the front page where the login form is.

I couldn't some of the matchers to work (happy to hear from you if you do), so there's room for improvement here.

require File.dirname(__FILE__) + '/../acceptance_helper'

describe 'URLs that require login' do  
  context 'the /mystore URL' do
    before :each do
      visit '/logout'

    it "lets you in if you're logged in" do
      store = Store.make! :password => 'test123'
      signin, 'test123'
      visit '/mystore'
      selector('#already-have-account').should be_nil

    it "redirects you to the home page if you're not logged in" do
      visit '/mystore'
      selector('#already-have-account').should_not be_nil


It turned out to be a lot easier than I expected, but I couldn't get some of the matchers to work. E.g.: page.should have_selector. Happy to hear from you if you figure it out.

I assume you can simply change the Capybara driver to Selenium, and have specs for testing front-end behavior as well. Which is quite awesome.

If you have any questions, ping me on Twitter or leave a comment.


This comment has been minimized.

Copy link

ashtija commented Apr 14, 2011

Thank you. This works fine, but I am trying to work with selenium (Sinatra, Rspec, Capybara and Selenium) but I am getting "Rack application timed out during boot" and its not executing browser :(. Please help


This comment has been minimized.

Copy link
Owner Author

juliocesar commented Apr 14, 2011

Hey. Capybara offers a DSL for doing integration tests which I think is what you should use rather than following this guide.



This comment has been minimized.

Copy link

ashtija commented Apr 15, 2011

Thanks, I was able to run test successfully on firefox. But it takes hell of a time to execute.


This comment has been minimized.

Copy link

vaz commented May 19, 2016

This post is pretty old but still shows up pretty high in search results... so:

Regarding getting the matchers like have_selector to work, you need another include:

config.include Capybara::DSL  # used to be just Capybara, that's deprecated now
config.include Capybara::RSpecMatchers

You should only need to require capybara/rspec in the spec helper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.