Testing a Sinatra App with Capybara


First things first. Add the following to your gemfile, if they’re not already there:

gem 'minitest' 
gem 'capybara'
gem 'launchy'
gem 'database_cleaner'

Minitest: Capybara uses minitest. You should have model tests that use it too (unless you’re using something like Rspec instead).

Capybara: obviously you need capybara to run capybara.

Launchy: this allows you to run the save_and_open_page_path command when writing your tests. When you do this, it saves a copy of the html page your test currently has you on, and opens it in a browser — used for debugging.

Database cleaner: allows you to wipe and reset your database at the beginning and end of each test. More on this later.

Test database

In order to ensure that you don’t corrupt your actual data, make sure your tests are running off of a different database than development and production. Like this:

if ENV["RACK_ENV"] == "test" 
database = Sequel.sqlite("db/skill_inventory_test.sqlite3")
database = Sequel.sqlite("db/skill_inventory_development.sqlite3") end
#The following line is then in the test_helper.rb file so that branch is followed:
ENV["RACK_ENV"] = test

Test Helper

When writing tests, you’ll need to know exactly what is in the database, as that will inform your assertions. The only way to be sure of this is to start with a clean slate for each test. We used Database cleaner to do this. You already have the gem in your Gemfile, so setup is once again in our test_helper file. The following is in that file:

ENV["RACK_ENV"] = "test" 
require File.expand_path('../../config/environment', __FILE__) require 'minitest/autorun'
require 'minitest/pride'
require 'capybara/dsl'
require 'tilt/erb' DatabaseCleaner[:sequel, {:connection => Sequel.sqlite("db/skill_inventory_test.sqlite3")}].strategy = :truncation
Capybara.app = SkillInventoryApp Capybara.save_and_open_page_path = '../../tmp/capybara' module TestHelpers
def setup
def teardown
def skill_inventory
database = Sequel.sqlite("db/skill_inventory_test.sqlite3")
@skill_inventory ||= SkillInventory.new(database)

The TestHelper module is included in each testing class. Minitest automatically runs both setup and teardown methods with each test, so you won’t need to call them manually.

Line 11 tells Capybara which controller to use when running the tests

Line 12 just tells the program where to save the html files that are generated when you run the save_and_open_page_path command, so you can add that file to your .gitignore file.

User Stories

Capybara tests are based on the user experience, which shouldn’t require any knowledge of what the code base looks like. First write a user story ( in the Agile sense of the word), and then write the test to represent it. The basic user story format is as follows:

  • As a [user] (What kind of user? Guest? Authenticated? New? Existing but unauthenticated?)
  • When I [do something]
  • And I [do something else] …(however many times you need to ‘and I…’)
  • Then I [expect this to happen]…(however many things you expect to happen)

Here’s a very basic example user story:

  • As a user (the app here is very basic and has no authentication, so all users are the same kind)
  • When I visit the home page
  • And I click on the ‘new skill’ link
  • And I fill in the ‘title’ field with a skill title
  • And I fill in the ‘description’ field with a skill description
  • And I select ‘school’ from the category drop-down menu
  • And I click submit
  • Then I should be redirected to the ‘skills’ page
  • Then I should see my skill listed there with the above title and category

Capybara syntax

All that’s left to do after that is to translate the above user story into Capybara. My whole test looks like this (each line of the test can effectively be mapped to a line of the user story, above):

require_relative '../test_helper' class UserCreatesSkillTest < Minitest::Test 
include Capybara::DSL
include TestHelpers
def test_creates_a_skill_with_valid_attributes
visit '/'
click_link('New Skill')
fill_in("skill[name]", with: "Skill name")
fill_in("skill[description]", with: "Skill description")
select "School", from: "category_id"
assert_equal '/skills', current_path within("#skill") do
assert page.has_content?("Skill name")
assert page.has_content?("school")
refute page.has_content?("work")

visit, fill_in, select, click_button, current_path, within, and has_content? are all built in Capybara methods. A more complete list of available methods and how to use them can be found here

All that’s left from there is to run the test and see the rainbow!

You should, of course, have tests for each piece of your CRUD functionality, both for when the user does everything as expected, as well as edge cases and exceptions. The process will be the same for all of these. Happy testing!

Senior Software Engineer | www.adriennedomingus.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store